Files
ComfyUI-Ethanfel-Prompt-Bui…/tools/prompt_smoke.py
T

9464 lines
466 KiB
Python

#!/usr/bin/env python3
"""Smoke-test core prompt routes without importing ComfyUI.
The checks here are intentionally lightweight invariants, not golden prompt
snapshots. They prove that representative rows still carry structured metadata
and that the Krea2, SDXL, and caption formatter paths consume metadata instead
of silently falling back to raw text parsing.
"""
from __future__ import annotations
import argparse
import json
import random
import re
import sys
import tempfile
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Callable
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
import caption_naturalizer # noqa: E402
import caption_format_route # noqa: E402
import caption_metadata_routes # noqa: E402
import caption_policy # noqa: E402
import caption_text_policy # noqa: E402
import builder_config_route # noqa: E402
import builder_prompt_route # noqa: E402
import cast_context # noqa: E402
import category_extensions # noqa: E402
import category_template_metadata # noqa: E402
import character_appearance # noqa: E402
import character_config # noqa: E402
import character_profile # noqa: E402
import character_slot # noqa: E402
import category_cast_config # noqa: E402
import category_library # noqa: E402
import filter_config # noqa: E402
import formatter_detail # noqa: E402
import formatter_input # noqa: E402
import formatter_target # noqa: E402
import hardcore_action_metadata # noqa: E402
import hardcore_position_config # noqa: E402
import __init__ as sxcp_nodes # noqa: E402
import generation_profile_config # noqa: E402
import hardcore_role_outercourse # noqa: E402
import index_switch_policy # noqa: E402
import item_axis_policy # noqa: E402
import node_tooltips # noqa: E402
import krea_cast # noqa: E402
import krea_action_details # noqa: E402
import krea_action_context # noqa: E402
import krea_configured_cast_formatter # noqa: E402
import krea_format_route # noqa: E402
import krea_formatter # noqa: E402
import krea_normal_formatter # noqa: E402
import krea_pair_formatter # noqa: E402
import krea_row_fields # noqa: E402
import location_config # noqa: E402
import loop_nodes # noqa: E402
import pair_builder # noqa: E402
import pair_camera # noqa: E402
import pair_cast # noqa: E402
import pair_clothing # noqa: E402
import pair_rows # noqa: E402
import prompt_hygiene # noqa: E402
import prompt_builder as pb # noqa: E402
import outercourse_action_policy # noqa: E402
import pov_policy # noqa: E402
import row_normalization # noqa: E402
import row_assembly # noqa: E402
import route_metadata # noqa: E402
import row_camera # noqa: E402
import row_category_route # noqa: E402
import row_expression # noqa: E402
import row_generation # noqa: E402
import row_item # noqa: E402
import row_location # noqa: E402
import row_pools # noqa: E402
import row_prompt_axes # noqa: E402
import row_rendering # noqa: E402
import row_role_graph # noqa: E402
import row_route_metadata # noqa: E402
import row_subject_route # noqa: E402
import scene_camera_adapters # noqa: E402
import server_routes # noqa: E402
import sdxl_formatter # noqa: E402
import sdxl_format_route # noqa: E402
import sdxl_presets # noqa: E402
import sdxl_tag_policy # noqa: E402
import sdxl_tag_routes # noqa: E402
import seed_config # noqa: E402
import krea_pov # noqa: E402
import subject_context # noqa: E402
from tools import prompt_route_simulation # noqa: E402
Trigger = "sxcppnl7"
SdxlTrigger = "mythp0rt"
@dataclass
class SmokeReport:
verbose: bool = True
passed: list[str] = field(default_factory=list)
failed: list[str] = field(default_factory=list)
def ok(self, name: str) -> None:
self.passed.append(name)
if self.verbose:
print(f"PASS {name}")
def fail(self, name: str, message: str) -> None:
detail = f"{name}: {message}"
self.failed.append(detail)
print(f"FAIL {detail}")
def _clean_key(value: str) -> str:
return re.sub(r"[^a-z0-9]+", " ", str(value or "").lower()).strip()
def _json(value: Any) -> str:
return json.dumps(value, ensure_ascii=True, sort_keys=True)
def _expect(condition: bool, message: str) -> None:
if not condition:
raise AssertionError(message)
def _expect_text(name: str, value: Any, min_len: int = 8) -> str:
text = str(value or "").strip()
_expect(len(text) >= min_len, f"{name} is empty or too short")
_expect("None" not in text, f"{name} leaked None")
_expect(" " not in text, f"{name} has repeated spaces")
_expect(" ," not in text and " ." not in text, f"{name} has bad punctuation spacing")
return text
def _expect_no_duplicate_comma_items(name: str, value: Any) -> None:
items = [_clean_key(part) for part in str(value or "").split(",")]
items = [part for part in items if part]
duplicates = sorted({part for part in items if items.count(part) > 1})
_expect(not duplicates, f"{name} has duplicate comma items: {duplicates[:5]}")
def _expect_no_softcore_noise(name: str, value: Any) -> None:
text = str(value or "").lower()
noisy = (
"the image focuses",
"softcore version",
"non-explicit teaser setup",
"no sex act",
"genital contact",
"keep the softcore version",
"focused on woman a alone",
)
found = [phrase for phrase in noisy if phrase in text]
_expect(not found, f"{name} has softcore prompt noise: {found}")
def _trigger_count(text: str, trigger: str) -> int:
return len(re.findall(rf"(?<![a-z0-9_]){re.escape(trigger)}(?![a-z0-9_])", text, flags=re.IGNORECASE))
def _expect_trigger_once(name: str, value: Any, trigger: str) -> None:
text = str(value or "")
count = _trigger_count(text, trigger)
_expect(count == 1, f"{name} should contain trigger {trigger!r} exactly once, got {count}")
def _expect_row_base(row: dict[str, Any], name: str) -> None:
_expect(isinstance(row, dict), f"{name} did not return a metadata row")
_expect_text(f"{name}.prompt", row.get("prompt"), 20)
_expect_text(f"{name}.negative_prompt", row.get("negative_prompt"), 8)
_expect_no_duplicate_comma_items(f"{name}.negative_prompt", row.get("negative_prompt"))
_expect(json.loads(_json(row)) == row, f"{name} is not JSON-stable")
def _expect_custom_row(row: dict[str, Any], name: str) -> None:
_expect_row_base(row, name)
_expect(row.get("source") == "json_category", f"{name}.source should be json_category")
_expect_text(f"{name}.item", row.get("item"), 8)
_expect_text(f"{name}.scene_text", row.get("scene_text"), 8)
_expect_text(f"{name}.composition", row.get("composition"), 8)
_expect_text(f"{name}.role_graph", row.get("source_role_graph") or row.get("role_graph"), 8)
_expect(isinstance(row.get("item_axis_values"), dict), f"{name}.item_axis_values missing")
_expect(isinstance(row.get("formatter_hints"), dict), f"{name}.formatter_hints missing")
def _expect_formatter_outputs(row: dict[str, Any], name: str, *, target: str = "auto") -> None:
metadata = _json(row)
krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target=target)
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata: {krea.get('method')}")
_expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 20)
_expect_no_duplicate_comma_items(f"{name}.krea_negative", krea.get("negative_prompt"))
sdxl = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=metadata,
target=target,
trigger=SdxlTrigger,
prepend_trigger=True,
)
_expect("metadata" in sdxl.get("method", ""), f"{name}.sdxl did not use metadata: {sdxl.get('method')}")
_expect_text(f"{name}.sdxl_prompt", sdxl.get("sdxl_prompt"), 20)
_expect_trigger_once(f"{name}.sdxl_prompt", sdxl.get("sdxl_prompt"), SdxlTrigger)
_expect_no_duplicate_comma_items(f"{name}.sdxl_negative", sdxl.get("negative_prompt"))
caption, method = caption_naturalizer.naturalize_caption(
"",
metadata_json=metadata,
target=target,
trigger=Trigger,
include_trigger=True,
)
_expect("metadata" in method, f"{name}.caption did not use metadata: {method}")
_expect_text(f"{name}.caption", caption, 20)
_expect_trigger_once(f"{name}.caption", caption, Trigger)
def _expect_krea_normal_route_parity(row: dict[str, Any], name: str, method: str) -> None:
typed_route = krea_normal_formatter.format_normal_row_result(
krea_formatter._krea_normal_row_request_from_row(row, "balanced", "preserve"),
krea_formatter._krea_normal_row_dependencies(),
)
legacy_route = krea_formatter._normal_row_to_krea(row, "balanced", "preserve")
_expect(
typed_route.as_tuple() == legacy_route,
f"{name} typed Krea normal formatter route should match legacy wrapper output",
)
_expect(typed_route.method == method, f"{name} typed Krea normal formatter method changed")
_expect_text(f"{name}.typed_krea_prompt", typed_route.prompt, 20)
def _character_cast(*, pov_man: bool = False) -> str:
cast = pb.build_character_slot_json(
subject_type="woman",
label="A",
age="25-year-old adult",
ethnicity="western_european",
figure="balanced",
body="slim",
descriptor_detail="full",
expression_intensity=0.65,
softcore_expression_intensity=0.45,
hardcore_expression_intensity=0.85,
)["character_cast"]
return pb.build_character_slot_json(
subject_type="man",
label="A",
age="40-year-old adult",
ethnicity="western_european",
figure="balanced",
body="average",
descriptor_detail="compact",
expression_intensity=0.55,
softcore_expression_intensity=0.35,
hardcore_expression_intensity=0.75,
presence_mode="pov" if pov_man else "visible",
character_cast=cast,
)["character_cast"]
def _character_cast_two_men(*, pov_first_man: bool = False) -> str:
cast = _character_cast(pov_man=pov_first_man)
return pb.build_character_slot_json(
subject_type="man",
label="B",
age="41-year-old adult",
ethnicity="western_european",
figure="balanced",
body="average",
descriptor_detail="compact",
expression_intensity=0.55,
softcore_expression_intensity=0.35,
hardcore_expression_intensity=0.75,
character_cast=cast,
)["character_cast"]
def _character_cast_subjects(subjects: list[str] | tuple[str, ...]) -> str:
cast = ""
counts = {"woman": 0, "man": 0}
for subject in subjects:
subject = str(subject)
counts[subject] += 1
label = chr(ord("A") + counts[subject] - 1)
cast = pb.build_character_slot_json(
subject_type=subject,
label=label,
age="25-year-old adult" if subject == "woman" else "40-year-old adult",
ethnicity="western_european",
figure="balanced",
body="slim" if subject == "woman" else "average",
descriptor_detail="compact",
expression_intensity=0.55,
softcore_expression_intensity=0.35,
hardcore_expression_intensity=0.75,
character_cast=cast,
)["character_cast"]
return cast
def _exact_subcategory_selector(category: dict[str, Any], subcategory: dict[str, Any]) -> str:
return category_library.exact_subcategory_selector(category, subcategory)
def _matrix_cast_for_route(category: dict[str, Any], subcategory: dict[str, Any]) -> tuple[int, int, str]:
subject_type = str(subcategory.get("subject_type") or category.get("subject_type") or "woman")
if subject_type == "woman":
return 1, 0, ""
if subject_type == "man":
return 0, 1, ""
if subject_type == "couple":
return 1, 1, ""
min_people = int(subcategory.get("min_people") or 0)
women_count = int(subcategory.get("min_women") or 0)
men_count = int(subcategory.get("min_men") or 0)
if min_people <= 1 and not women_count and not men_count:
women_count = 1
elif min_people >= 4:
women_count = max(women_count, 2)
men_count = max(men_count, min_people - women_count)
elif min_people >= 3:
women_count = max(women_count, 1)
men_count = max(men_count, min_people - women_count)
elif min_people >= 2:
women_count = max(women_count, 1)
men_count = max(men_count, min_people - women_count)
required_total = max(1, min_people)
if women_count + men_count < required_total:
men_count += required_total - (women_count + men_count)
if women_count == 0 and men_count == 0:
women_count = 1
cast = _character_cast_subjects(["woman"] * women_count + ["man"] * men_count)
return women_count, men_count, cast
def _action_filter(focus: str, hardcore_position_config: str | dict[str, Any] | None = "") -> str:
kwargs = {
"allow_toys": False,
"allow_double": False,
"allow_penetration": focus in ("penetration_only", "keep_pool"),
"allow_foreplay": focus in ("foreplay_only", "keep_pool"),
"allow_interaction": focus in ("interaction_only", "keep_pool"),
"allow_manual": focus in ("manual_only", "keep_pool"),
"allow_oral": focus in ("oral_only", "keep_pool"),
"allow_outercourse": focus in ("outercourse_only", "keep_pool"),
"allow_anal": focus in ("anal_only", "keep_pool"),
"allow_climax": focus in ("climax_only", "keep_pool"),
}
return pb.build_hardcore_action_filter_json(
hardcore_position_config=hardcore_position_config,
focus=focus,
**kwargs,
)
def _broad_hardcore_filter() -> str:
return pb.build_hardcore_action_filter_json(
focus="keep_pool",
allow_toys=True,
allow_double=True,
allow_penetration=True,
allow_foreplay=True,
allow_interaction=True,
allow_manual=True,
allow_oral=True,
allow_outercourse=True,
allow_anal=True,
allow_climax=True,
)
def _position_filter(focus: str, family: str, positions: list[str] | tuple[str, ...] | str) -> str:
position_config = pb.build_hardcore_position_pool_json(
combine_mode="replace",
family=family,
selected_positions=positions,
)
return _action_filter(focus, position_config)
def _anal_double_filter(positions: list[str] | tuple[str, ...] | str) -> str:
position_config = pb.build_hardcore_position_pool_json(
combine_mode="replace",
family="anal",
selected_positions=positions,
)
return pb.build_hardcore_action_filter_json(
hardcore_position_config=position_config,
focus="anal_only",
allow_toys=True,
allow_double=True,
allow_penetration=True,
allow_foreplay=False,
allow_interaction=False,
allow_manual=False,
allow_oral=False,
allow_outercourse=False,
allow_anal=True,
allow_climax=False,
)
def _coworking_location_config() -> str:
return pb.build_location_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_locations=(
"coworking_smoke: coworking lounge with tall windows, warm desks, "
"laptop tables, glass partition seams, repeated desk rows, plants, "
"and soft shared-office depth"
),
)
def _classical_library_theme_configs() -> tuple[str, str]:
return _thematic_location_configs("classical_library")
def _thematic_location_configs(theme: str) -> tuple[str, str]:
location_config, composition_config, _summary = pb.build_thematic_location_json(
enabled=True,
combine_mode="replace",
theme=theme,
)
return location_config, composition_config
def _orbit_camera(
*,
horizontal_angle: int,
vertical_angle: int,
zoom: float,
subject_focus: str = "auto",
camera_detail: str = "compact",
) -> str:
return pb.build_camera_orbit_config_json(
enabled=True,
camera_mode="standard",
horizontal_angle=horizontal_angle,
vertical_angle=vertical_angle,
zoom=zoom,
framing="from_zoom",
subject_focus=subject_focus,
lens="auto",
orientation="auto",
phone_visibility="auto",
priority="strong",
camera_detail=camera_detail,
include_degrees=True,
)
def _prompt_row(
*,
name: str,
category: str,
subcategory: str,
seed: int,
seed_config: str | dict[str, Any] | None = None,
character_cast: str = "",
women_count: int = 1,
men_count: int = 1,
hardcore_position_config: str = "",
camera_config: str | dict[str, Any] | None = "",
location_config: str | dict[str, Any] | None = "",
composition_config: str | dict[str, Any] | None = "",
style_config: str | dict[str, Any] | None = "",
) -> dict[str, Any]:
row = pb.build_prompt(
category=category,
subcategory=subcategory,
row_number=1,
start_index=1,
seed=seed,
clothing="random",
ethnicity="any",
poses="random",
backside_bias=0.35,
figure="random",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=0.5,
standard_pose_ratio=0.5,
trigger=Trigger,
prepend_trigger_to_prompt=True,
extra_positive="",
extra_negative="",
seed_config=seed_config,
character_cast=character_cast,
women_count=women_count,
men_count=men_count,
expression_enabled=True,
expression_intensity=0.6,
hardcore_position_config=hardcore_position_config,
camera_config=camera_config,
location_config=location_config,
composition_config=composition_config,
style_config=style_config,
)
_expect_row_base(row, name)
return row
def _fixture_hardcore_row(**overrides: Any) -> dict[str, Any]:
row: dict[str, Any] = {
"source": "json_category",
"prompt": "Fixture explicit adult prompt for metadata route.",
"caption": "fixture caption",
"negative_prompt": "low quality, bad anatomy",
"main_category": "Hardcore sexual poses",
"subcategory": "Penetrative sex",
"category_slug": "hardcore_sexual_poses",
"subcategory_slug": "penetrative_sex",
"subject_type": "configured_cast",
"subject_phrase": "1 adult woman and 1 adult man",
"cast_summary": "1 woman, 1 man",
"cast_descriptor_text": (
"Woman A: 25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes; "
"Man A: 40-year-old adult man, average figure, tan skin, dark hair"
),
"cast_descriptors": [
"Woman A: 25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes",
"Man A: 40-year-old adult man, average figure, tan skin, dark hair",
],
"women_count": 1,
"men_count": 1,
"person_count": 2,
"item": (
"missionary position while full-body penetrative sex, hands gripping the ass, "
"mouth close to the ear, and explicit genital contact visible"
),
"custom_item": "Penetrative sex",
"item_label": "Sexual pose",
"item_axis_values": {
"position": "missionary position",
"penetration_act": "full-body penetrative sex",
"mouth_detail": "mouth close to the ear",
},
"item_template_metadata": {},
"formatter_hints": {},
"scene_text": "private studio room with warm light",
"scene_kind": "explicit adult sex scene",
"pose": "configured explicit pose",
"composition": "front-facing full-body frame",
"source_composition": "front-facing full-body frame",
"role_graph": (
"Woman A lies on her back with legs open around Man A's hips while Man A is above her between her thighs; "
"Man A's hips press close and Man A's penis thrusts into her pussy."
),
"source_role_graph": (
"Woman A lies on her back with legs open around Man A's hips while Man A is above her between her thighs; "
"Man A's hips press close and Man A's penis thrusts into her pussy."
),
"expression": "focused adult expression",
"action_family": "penetration",
"position_family": "penetrative",
"position_key": "missionary",
"position_keys": ["missionary"],
}
row.update(overrides)
return row
def smoke_builtin_single() -> None:
row = _prompt_row(name="builtin_single_woman", category="woman", subcategory="random", seed=1001, men_count=0)
_expect(row.get("source") == "built_in_generator", "builtin row should come from built-in generator")
_expect(row.get("subject_type") == "woman", "builtin single row lost normalized subject_type")
_expect(row.get("subject_phrase") == "woman", "builtin single row lost normalized subject_phrase")
_expect(row.get("women_count") == 1 and row.get("men_count") == 0, "builtin single row lost normalized cast counts")
_expect(row.get("person_count") == 1, "builtin single row lost normalized person count")
_expect_text("builtin_single_woman.scene_text", row.get("scene_text"), 12)
_expect(row.get("scene_slug") == row.get("scene"), "builtin single row lost legacy scene slug metadata")
_expect(row.get("scene_entry", {}).get("slug") == row.get("scene"), "builtin single row lost scene_entry slug")
item = _expect_text("builtin_single_woman.item", row.get("item"), 8)
pose = _expect_text("builtin_single_woman.pose", row.get("pose"), 8)
body_phrase = _expect_text("builtin_single_woman.body_phrase", row.get("body_phrase"), 8)
skin = _expect_text("builtin_single_woman.skin", row.get("skin"), 8)
hair = _expect_text("builtin_single_woman.hair", row.get("hair"), 5)
eyes = _expect_text("builtin_single_woman.eyes", row.get("eyes"), 4)
_expect(row.get("item_label") == "Clothing", "builtin single row lost item label")
_expect(row.get("clothing") == item, "builtin single row did not mirror clothing into item metadata")
_expect("fashion editorial styling" not in item.lower(), "builtin single item kept generic styling suffix")
_expect("cast_summary" not in row, "builtin single row should not masquerade as configured cast")
_expect_trigger_once("builtin_single_woman.prompt", row.get("prompt"), Trigger)
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
_expect(row.get("scene_text") in str(krea.get("krea_prompt", "")), "builtin single Krea route used scene slug instead of scene text")
sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(row), target="single", trigger=SdxlTrigger, prepend_trigger=True)
_expect("1woman" in str(sdxl.get("sdxl_prompt", "")).lower(), "builtin single SDXL route lost normalized woman count")
sdxl_scene_text = str(sdxl.get("sdxl_prompt", "")).lower()
scene_words = re.findall(r"[a-z0-9]+", str(row.get("scene_text", "")).split(",", 1)[0].lower())
scene_anchor = " ".join(scene_words[:2])
_expect(scene_anchor in sdxl_scene_text, "builtin single SDXL route lost readable scene text")
_expect(str(row.get("scene_slug", "")).lower() not in sdxl_scene_text, "builtin single SDXL route leaked raw scene slug")
caption, caption_method = caption_naturalizer.naturalize_caption("", metadata_json=_json(row), target="single", trigger=Trigger, include_trigger=True)
_expect(caption_method.endswith("metadata(single)"), "builtin single caption route did not use single metadata branch")
_expect("woman" in caption.lower(), "builtin single caption route lost normalized subject")
_expect(row.get("scene_text") in caption, "builtin single caption route used scene slug instead of scene text")
metadata_only = dict(row)
metadata_only["prompt"] = ""
metadata_only["caption"] = ""
krea_metadata = krea_formatter.format_krea2_prompt("", metadata_json=_json(metadata_only), target="single")
sdxl_metadata = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(metadata_only), target="single", trigger=SdxlTrigger, prepend_trigger=True)
caption_metadata, caption_metadata_method = caption_naturalizer.naturalize_caption(
"",
metadata_json=_json(metadata_only),
target="single",
trigger=Trigger,
include_trigger=True,
)
_expect(item in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost explicit item")
_expect(pose in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost explicit pose")
krea_metadata_prompt = str(krea_metadata.get("krea_prompt", ""))
if re.search(r"\b(?:body|build|figure|frame|physique|silhouette)\s+with\b", body_phrase, flags=re.IGNORECASE):
body_head, body_detail = body_phrase.split(" with ", 1)
_expect(
body_head in krea_metadata_prompt and body_detail in krea_metadata_prompt,
"Krea metadata-only built-in route lost normalized body phrase",
)
else:
_expect(body_phrase in krea_metadata_prompt, "Krea metadata-only built-in route lost body phrase")
_expect(skin in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost skin")
_expect(hair in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost hair")
_expect(eyes in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost eyes")
item_anchor = " ".join(re.findall(r"[a-z0-9]+", item.lower())[:3])
pose_anchor = " ".join(re.findall(r"[a-z0-9]+", pose.lower())[:4])
sdxl_metadata_prompt = str(sdxl_metadata.get("sdxl_prompt", "")).lower()
_expect(item_anchor in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost explicit item")
_expect(pose_anchor in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost explicit pose")
for body_tag in sdxl_tag_policy.split_tag_text(body_phrase):
_expect(body_tag.lower() in sdxl_metadata_prompt, f"SDXL metadata-only built-in route lost body tag: {body_tag}")
_expect(skin.lower() in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost skin")
_expect(hair.lower() in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost hair")
_expect(eyes.lower() in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost eyes")
_expect(caption_metadata_method.endswith("metadata(single)"), "Caption metadata-only built-in route did not use single metadata branch")
_expect(item in caption_metadata and pose in caption_metadata, "Caption metadata-only built-in route lost explicit item or pose")
_expect(body_phrase in caption_metadata and skin in caption_metadata, "Caption metadata-only built-in route lost appearance")
_expect(hair in caption_metadata and eyes in caption_metadata, "Caption metadata-only built-in route lost hair or eyes")
_expect_formatter_outputs(row, "builtin_single_woman", target="single")
def smoke_builtin_couple_metadata() -> None:
row = _prompt_row(name="builtin_couple", category="couple", subcategory="random", seed=1003, men_count=0)
_expect(row.get("source") == "built_in_generator", "builtin couple row should come from built-in generator")
_expect(row.get("subject_type") == "couple", "builtin couple row lost normalized subject_type")
_expect(row.get("women_count") + row.get("men_count") == row.get("person_count"), "builtin couple row lost count consistency")
_expect(row.get("person_count") == 2, "builtin couple row lost normalized person count")
subject = _expect_text("builtin_couple.subject_phrase", row.get("subject_phrase"), 5)
age = _expect_text("builtin_couple.age_band", row.get("age_band"), 8)
body = _expect_text("builtin_couple.body_type", row.get("body_type"), 5)
scene = _expect_text("builtin_couple.scene_text", row.get("scene_text"), 12)
item = _expect_text("builtin_couple.item", row.get("item"), 8)
pose = _expect_text("builtin_couple.pose", row.get("pose"), 8)
expression = _expect_text("builtin_couple.expression", row.get("expression"), 8)
composition = _expect_text("builtin_couple.composition", row.get("composition"), 8)
metadata_only = dict(row)
metadata_only["prompt"] = ""
metadata_only["caption"] = ""
metadata = _json(metadata_only)
krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target="auto")
sdxl = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=metadata,
target="auto",
trigger=SdxlTrigger,
prepend_trigger=True,
)
caption, caption_method = caption_naturalizer.naturalize_caption(
"",
metadata_json=metadata,
target="auto",
trigger=Trigger,
include_trigger=True,
)
clean_age = caption_text_policy.clean_age_phrase(age)
krea_prompt = str(krea.get("krea_prompt", ""))
sdxl_prompt = str(sdxl.get("sdxl_prompt", "")).lower()
_expect("metadata(couple)" in krea.get("method", ""), "Krea built-in couple route did not use metadata")
_expect(subject.capitalize() in krea_prompt and clean_age in krea_prompt, "Krea built-in couple route lost subject or age")
_expect("all visibly adult" not in krea_prompt.lower(), "Krea built-in couple route kept redundant adult noise")
_expect("Age detail:" not in krea_prompt, "Krea built-in couple route kept age helper label")
for value in (body, scene, item, pose, expression, composition):
_expect(value in krea_prompt, f"Krea built-in couple route lost metadata value: {value}")
_expect("2women" in sdxl_prompt or "1woman, 1man" in sdxl_prompt or "2men" in sdxl_prompt, "SDXL built-in couple route lost count tags")
for value in (body, scene, item, pose, expression, composition):
for tag in sdxl_tag_policy.split_tag_text(value):
_expect(tag.lower() in sdxl_prompt, f"SDXL built-in couple route lost metadata tag: {tag}")
_expect(caption_method.endswith("metadata(couple)"), "Caption built-in couple route did not use metadata")
_expect(clean_age in caption and scene in caption, "Caption built-in couple route lost age or scene")
_expect("The age detail is" not in caption, "Caption built-in couple route kept age helper sentence")
for value in (body, item, pose, expression, composition):
_expect(value in caption, f"Caption built-in couple route lost metadata value: {value}")
def smoke_camera_scene_single() -> None:
row = _prompt_row(
name="camera_scene_single",
category="woman",
subcategory="random",
seed=1051,
men_count=0,
camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=-30,
zoom=5.0,
subject_focus="environment",
),
location_config=_coworking_location_config(),
)
scene_directive = _expect_text("camera_scene_single.camera_scene_directive", row.get("camera_scene_directive"), 40)
camera_directive = _expect_text("camera_scene_single.camera_directive", row.get("camera_directive"), 20)
_expect("Coworking camera layout" in scene_directive, "single camera-scene adapter did not identify coworking layout")
_expect("front-right quarter view" in scene_directive, "single camera scene missed orbit direction")
_expect("low-angle shot" in scene_directive, "single camera scene missed orbit elevation")
_expect("45-degree front-right quarter view" in camera_directive, "single camera directive missed custom orbit prompt")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
prompt = krea.get("krea_prompt") or ""
_expect("Coworking camera layout" in prompt, "Krea single prompt lost camera-scene directive")
_expect("45-degree front-right quarter view" in prompt, "Krea single prompt lost camera directive")
_expect_formatter_outputs(row, "camera_scene_single", target="single")
custom_location = pb.build_location_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_locations=json.dumps(
{
"slug": "greenhouse_suite",
"prompt": "private room with soft daylight",
"camera_profile": {
"key": "glass_conservatory",
"family": "greenhouse",
"layout_label": "Glass conservatory camera layout",
"place": "glass conservatory",
"foreground": "plant shelf edge, fern leaves, and iron table corner",
"midground": "glass panes, iron ribs, and potted palms",
"background": "hanging vines, greenhouse windows, and layered plant depth",
"detail_label": "conservatory details",
"composition": {
"woman": "glass conservatory frame with the woman beside fern leaves and greenhouse depth behind her",
"default": "glass conservatory frame with the subjects beside fern leaves and greenhouse depth behind them",
},
},
},
sort_keys=True,
),
)
custom_composition = pb.build_composition_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_compositions=json.dumps({"prompt": "polished mirror view with bag and shoes visible"}, sort_keys=True),
)
custom_row = _prompt_row(
name="camera_scene_custom_inline_profile",
category="woman",
subcategory="random",
seed=1061,
men_count=0,
camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=30,
zoom=5.0,
subject_focus="environment",
),
location_config=custom_location,
composition_config=custom_composition,
)
custom_scene = _expect_text(
"camera_scene_custom_inline_profile.camera_scene_directive",
custom_row.get("camera_scene_directive"),
40,
)
custom_composition_text = _expect_text(
"camera_scene_custom_inline_profile.composition",
custom_row.get("composition"),
20,
)
_expect("Glass conservatory camera layout" in custom_scene, "custom Location Pool JSON camera profile did not drive scene layout")
_expect(custom_row.get("scene_camera_profile_key") == "glass_conservatory", "custom Location Pool JSON profile key was not exposed")
_expect(custom_row.get("scene_entry", {}).get("camera_profile", {}).get("family") == "greenhouse", "custom Location Pool JSON profile metadata was not preserved")
_expect("glass conservatory" in custom_composition_text.lower(), "custom Location Pool JSON profile did not clean composition")
_expect("bag" not in custom_composition_text.lower() and "shoes" not in custom_composition_text.lower(), "custom inline profile composition leaked unrelated props")
def smoke_scene_camera_adapter_pov_profile_policy() -> None:
parsed_camera = {
"camera_mode": "standard",
"camera_detail": "compact",
"orbit_direction": "front-right quarter view",
"orbit_elevation_label": "elevated shot",
"orbit_distance_label": "medium shot",
"custom_camera_prompt": "front-right quarter view, elevated shot, medium shot",
}
for profile in scene_camera_adapters.SCENE_CAMERA_PROFILES:
key = str(profile.get("key") or "")
foreground = str(profile.get("foreground") or "")
non_pov = _expect_text(
f"scene_camera_adapter_pov_profile_policy.{key}.non_pov",
scene_camera_adapters.scene_camera_directive(
"",
parsed_camera,
pov_labels=[],
subject_kind="couple",
profile_key=key,
),
40,
)
pov = _expect_text(
f"scene_camera_adapter_pov_profile_policy.{key}.pov",
scene_camera_adapters.scene_camera_directive(
"",
parsed_camera,
pov_labels=["Man A"],
subject_kind="couple",
profile_key=key,
),
40,
)
_expect(foreground and foreground in non_pov, f"{key} non-POV scene directive lost profile foreground anchor")
_expect(foreground not in pov, f"{key} POV scene directive reused profile foreground anchor as viewer-side text")
_expect("from POV" in pov, f"{key} POV scene directive lost POV marker")
_expect(
"lower foreground is reserved for POV body or hand cues" in pov,
f"{key} POV scene directive lost lower-foreground body-cue reservation",
)
plural_directive = scene_camera_adapters.scene_camera_directive(
"",
parsed_camera,
pov_labels=[],
subject_kind="subjects",
profile_key="coworking_lounge",
)
_expect(
"the subjects are placed" in plural_directive,
"scene camera adapter used singular grammar for plural subjects",
)
_expect("the subjects is" not in plural_directive, "scene camera adapter leaked 'the subjects is'")
def smoke_row_camera_policy() -> None:
row = {
"prompt": "A generated adult prompt. Composition: vertical office-lobby walking composition. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, office-lobby walking composition, illustration",
"scene_text": "coworking lounge with tall windows, warm desks, and a polished outfit-check angle",
"composition": "office-lobby walking composition",
"subject_type": "configured_cast",
"women_count": 1,
"men_count": 1,
"pov_character_labels": ["Man A"],
}
updated = row_camera.apply_camera_config(
row,
_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
_expect(updated.get("camera_directive") == "", "POV row camera policy should suppress normal camera directive")
scene_directive = _expect_text("row_camera_policy.camera_scene_directive", updated.get("camera_scene_directive"), 40)
_expect("Coworking camera layout from POV" in scene_directive, "row camera policy missed POV coworking layout")
_expect("first-person spatial geometry" in scene_directive, "row camera policy lost POV geometry instruction")
_expect("Camera:" not in updated.get("prompt", ""), "row camera policy should not add normal Camera label")
_expect("45-degree front-right quarter view" not in updated.get("caption", ""), "POV row camera policy should not append camera caption")
_expect(
"coworking lounge frame with the couple near a desk edge" in updated.get("composition", ""),
"row camera policy did not adapt coworking composition for couple rows",
)
already_matching_row = dict(row)
already_matching_row["pov_character_labels"] = []
already_matching_row["composition"] = "coworking lounge frame with the subjects near a desk edge and tall-window depth behind them"
already_matching_row["prompt"] = (
"A generated adult prompt. Framed as coworking lounge frame with the subjects near a desk edge and tall-window depth behind them. "
"Avoid: low quality."
)
updated_matching = row_camera.apply_camera_config(
already_matching_row,
_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
_expect(
"the couple near a desk edge" in str(updated_matching.get("composition", "")),
"row camera policy did not adapt generic matching composition subject wording",
)
_expect(
"the subjects near a desk edge" not in str(updated_matching.get("prompt", "")),
"row camera policy left generic matching composition subject wording in prompt",
)
pre_normalized_row = dict(row)
pre_normalized_row["pov_character_labels"] = []
pre_normalized_row["composition"] = "coworking lounge frame with the woman near a desk edge and tall-window depth behind them"
pre_normalized_row["prompt"] = (
"A generated adult prompt. Framed as coworking lounge frame with the woman near a desk edge and tall-window depth behind them. "
"Avoid: low quality."
)
updated_pre_normalized = row_camera.apply_camera_config(
pre_normalized_row,
_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
_expect(
"the couple near a desk edge" in str(updated_pre_normalized.get("composition", "")),
"row camera policy did not adapt pre-normalized woman composition for couple rows",
)
_expect(
"the woman near a desk edge" not in str(updated_pre_normalized.get("prompt", "")),
"row camera policy left pre-normalized woman composition wording in prompt",
)
stale_internal_row = dict(row)
stale_internal_row["pov_character_labels"] = []
stale_internal_row["composition"] = "camera-aware coworking lounge frame with subjects near a desk edge"
stale_internal_row["prompt"] = (
"A generated adult prompt. Framed as camera-aware coworking lounge frame with subjects near a desk edge. "
"Avoid: low quality."
)
updated_stale_internal = row_camera.apply_camera_config(
stale_internal_row,
_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
_expect(
"camera-aware" not in str(updated_stale_internal.get("composition", "")).lower(),
"row camera policy leaked internal camera-aware composition wording",
)
_expect(
"camera-aware" not in str(updated_stale_internal.get("prompt", "")).lower(),
"row camera policy left internal camera-aware wording in prompt",
)
library_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
"scene_text": "grand classical library hall with towering dark-wood bookshelves, carved columns, rolling ladders, marble floor, and warm brass lamps",
"composition": "polished mirror view with bag and shoes visible",
"subject_type": "woman",
"women_count": 1,
"men_count": 0,
}
updated_library = row_camera.apply_camera_config(
library_row,
_orbit_camera(horizontal_angle=315, vertical_angle=0, zoom=5.0),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
library_scene = _expect_text("row_camera_policy.library_scene", updated_library.get("camera_scene_directive"), 40)
library_composition = _expect_text("row_camera_policy.library_composition", updated_library.get("composition"), 20)
_expect("Library camera layout" in library_scene, "row camera policy missed library layout")
_expect("front-left quarter view" in library_scene, "row camera library layout missed orbit direction")
_expect("bookshelf" in library_scene.lower() or "bookshelves" in library_scene.lower(), "row camera library layout missed shelf anchors")
_expect("bag" not in library_composition.lower(), "row camera library composition leaked bag wording")
_expect("shoes" not in library_composition.lower(), "row camera library composition leaked shoes wording")
_expect("library" in library_composition.lower(), "row camera library composition did not become location-aware")
semi_public_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
"scene_text": "upscale hotel corridor with repeating numbered doors, patterned carpet, brass wall lamps, luggage carts, and a secluded corner near a service alcove",
"scene_entry": {
"slug": "hotel_corridor_affair",
"prompt": "upscale hotel corridor with repeating numbered doors, patterned carpet, brass wall lamps, luggage carts, and a secluded corner near a service alcove",
"theme": "semi_public_affair",
},
"scene_theme": "semi_public_affair",
"composition": "polished mirror view with bag and shoes visible",
"subject_type": "configured_cast",
"women_count": 1,
"men_count": 1,
"pov_character_labels": ["Man A"],
}
updated_semi_public = row_camera.apply_camera_config(
semi_public_row,
_orbit_camera(horizontal_angle=180, vertical_angle=30, zoom=7.5),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
semi_public_scene = _expect_text("row_camera_policy.semi_public_scene", updated_semi_public.get("camera_scene_directive"), 40)
semi_public_composition = _expect_text(
"row_camera_policy.semi_public_composition",
updated_semi_public.get("composition"),
20,
)
_expect("Hotel corridor camera layout from POV" in semi_public_scene, "row camera semi-public scene did not use hotel corridor profile")
_expect("back view" in semi_public_scene, "row camera semi-public scene missed orbit direction")
_expect("first-person spatial geometry" in semi_public_scene, "row camera semi-public POV scene lost first-person geometry")
_expect(updated_semi_public.get("scene_camera_profile_key") == "hotel_corridor", "row camera semi-public scene did not expose text-matched profile key")
_expect("hotel corridor" in semi_public_composition.lower(), "row camera semi-public composition did not become location-aware")
_expect("bag" not in semi_public_composition.lower() and "shoes" not in semi_public_composition.lower(), "row camera semi-public composition leaked outfit-check props")
metadata_profile_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
"scene_text": "private themed room with neutral walls and warm lamps",
"scene_entry": {
"slug": "library_by_metadata",
"prompt": "private themed room with neutral walls and warm lamps",
"theme": "classical_library",
},
"scene_theme": "classical_library",
"composition": "polished mirror view with bag and shoes visible",
"subject_type": "woman",
"women_count": 1,
"men_count": 0,
}
updated_metadata_profile = row_camera.apply_camera_config(
metadata_profile_row,
_orbit_camera(horizontal_angle=315, vertical_angle=0, zoom=5.0),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
metadata_scene = _expect_text(
"row_camera_policy.metadata_scene",
updated_metadata_profile.get("camera_scene_directive"),
40,
)
_expect("Library camera layout" in metadata_scene, "row camera should prefer scene theme metadata over generic scene text")
_expect(
updated_metadata_profile.get("scene_camera_profile_key") == "classical_library",
"row camera should expose metadata-selected profile key",
)
_expect(
"library" in str(updated_metadata_profile.get("composition", "")).lower(),
"row camera metadata-selected profile did not clean composition",
)
explicit_profile_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
"scene_text": "coworking lounge with tall windows, warm desks, and glass partitions",
"scene_camera_profile_key": "classical_library",
"composition": "polished mirror view with bag and shoes visible",
"subject_type": "woman",
"women_count": 1,
"men_count": 0,
}
updated_explicit_profile = row_camera.apply_camera_config(
explicit_profile_row,
_orbit_camera(horizontal_angle=315, vertical_angle=0, zoom=5.0),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
_expect(
"Library camera layout" in str(updated_explicit_profile.get("camera_scene_directive", "")),
"explicit scene_camera_profile_key should override text-matched scene profile",
)
inline_profile_row = {
"prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
"scene_text": "private room with soft daylight",
"scene_entry": {
"slug": "greenhouse_room",
"prompt": "private room with soft daylight",
"camera_profile": {
"key": "glass_conservatory",
"family": "greenhouse",
"layout_label": "Glass conservatory camera layout",
"place": "glass conservatory",
"foreground": "plant shelf edge, fern leaves, and iron table corner",
"midground": "glass panes, iron ribs, and potted palms",
"background": "hanging vines, greenhouse windows, and layered plant depth",
"detail_label": "conservatory details",
"composition": {
"woman": "glass conservatory frame with the woman beside fern leaves and greenhouse depth behind her",
"default": "glass conservatory frame with the subjects beside fern leaves and greenhouse depth behind them",
},
},
},
"composition": "polished mirror view with bag and shoes visible",
"subject_type": "woman",
"women_count": 1,
"men_count": 0,
}
updated_inline_profile = row_camera.apply_camera_config(
inline_profile_row,
_orbit_camera(horizontal_angle=45, vertical_angle=30, zoom=5.0),
compact_labels=pb.CAMERA_COMPACT_LABELS,
)
inline_scene = _expect_text(
"row_camera_policy.inline_profile_scene",
updated_inline_profile.get("camera_scene_directive"),
40,
)
inline_composition = _expect_text(
"row_camera_policy.inline_profile_composition",
updated_inline_profile.get("composition"),
20,
)
inline_profile = updated_inline_profile.get("scene_camera_profile") if isinstance(updated_inline_profile.get("scene_camera_profile"), dict) else {}
_expect("Glass conservatory camera layout" in inline_scene, "inline scene camera profile did not drive camera layout")
_expect(updated_inline_profile.get("scene_camera_profile_key") == "glass_conservatory", "inline profile key was not exposed")
_expect(inline_profile.get("family") == "greenhouse", "inline profile family was not exposed")
_expect("glass conservatory" in inline_composition.lower(), "inline profile did not drive composition cleanup")
_expect("bag" not in inline_composition.lower() and "shoes" not in inline_composition.lower(), "inline profile composition leaked unrelated props")
beach_profile = scene_camera_adapters.scene_camera_profile(
"beach cafe table with woven chairs, linen shade, and ocean light in the background",
scene_entry={"slug": "beach_cafe_table"},
)
subway_profile = scene_camera_adapters.scene_camera_profile(
"clean subway platform with tiled walls, overhead lights, and a quiet selfie corner",
scene_entry={"slug": "subway_tile_selfie_corner"},
)
apartment_profile = scene_camera_adapters.scene_camera_profile(
"sunny apartment corner with bookshelves, a warm rug, and a phone on a small tripod",
scene_entry={"slug": "sunny_apartment_phone_tripod"},
)
_expect(not beach_profile, "scene camera resolver should not classify beach cafe as business cafe")
_expect(not subway_profile, "scene camera resolver should not classify subway tile as station lockers")
_expect(not apartment_profile, "scene camera resolver should not classify apartment bookshelves as classical library")
mirror_profile = scene_camera_adapters.scene_camera_profile(
"large bedroom mirror with the phone visible, bed behind the subject, and warm side lamps",
scene_entry={"slug": "large_bedroom_mirror_selfie"},
)
studio_profile = scene_camera_adapters.scene_camera_profile(
"dark private studio with glossy black floor reflections, rim light, and a phone tripod",
scene_entry={"slug": "black_latex_studio_floor"},
)
_expect(mirror_profile.get("key") == "mirror_room", "scene slug resolver missed mirror-room profile")
_expect(studio_profile.get("key") == "private_studio", "scene slug resolver missed private-studio profile")
def smoke_config_route_location_theme() -> None:
location_config, composition_config = _classical_library_theme_configs()
row = pb.build_prompt_from_configs(
row_number=1,
start_index=1,
seed=3301,
category_config=pb.build_category_config_json("hardcore_pose", "Foreplay and teasing"),
cast_config=pb.build_cast_config_json("mixed_couple"),
generation_profile=pb.build_generation_profile_json(
profile="hardcore_intense",
trigger_policy="prepend_trigger",
),
filter_config=pb.build_ethnicity_list_json(
include_french_european=True,
strict_excludes=True,
)["filter_config"],
seed_config=pb.build_seed_lock_config_json(
base_seed=3301,
reroll_axis="pose",
reroll_seed=3302,
),
camera_config=_orbit_camera(
horizontal_angle=315,
vertical_angle=0,
zoom=5.0,
subject_focus="action",
),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("foreplay_only"),
location_config=location_config,
composition_config=composition_config,
)
_expect_custom_row(row, "config_route_location_theme")
_expect(row.get("subcategory") == "Foreplay and teasing", "config route did not preserve requested subcategory")
_expect(row.get("subject_type") == "configured_cast", "config route did not apply character cast")
scene = _expect_text("config_route_location_theme.scene_text", row.get("scene_text"), 20)
composition = _expect_text("config_route_location_theme.composition", row.get("composition"), 10)
camera = _expect_text("config_route_location_theme.camera_directive", row.get("camera_directive"), 20)
scene_directive = _expect_text("config_route_location_theme.camera_scene_directive", row.get("camera_scene_directive"), 40)
scene_profile = row.get("scene_camera_profile") if isinstance(row.get("scene_camera_profile"), dict) else {}
_expect("library" in scene.lower() or "bookshelves" in scene.lower(), "location theme did not drive scene")
_expect("books" in composition.lower() or "shelf" in composition.lower() or "library" in composition.lower(), "location theme did not drive composition")
_expect(row.get("location_theme") == "classical_library", "location theme did not survive into row metadata")
_expect(row.get("scene_theme") == "classical_library", "selected scene theme did not survive into row metadata")
_expect(row.get("composition_theme") == "classical_library", "composition theme did not survive into row metadata")
_expect(row.get("scene_entry", {}).get("theme") == "classical_library", "selected scene entry lost theme metadata")
_expect("Library camera layout" in scene_directive, "location theme did not drive library camera-scene adapter")
_expect(row.get("scene_camera_profile_key") == "classical_library", "row lost scene camera profile key")
_expect(scene_profile.get("family") == "library", "row lost scene camera profile family")
_expect("front-left quarter view" in scene_directive, "library camera-scene adapter missed orbit direction")
_expect("bag" not in composition.lower() and "shoes" not in composition.lower(), "location theme composition leaked outfit-check props")
_expect("315-degree front-left quarter view" in camera, "config route did not preserve orbit camera directive")
seed_config = row.get("seed_config") if isinstance(row.get("seed_config"), dict) else {}
_expect(seed_config.get("pose_seed") == 3302, "seed lock did not reroll pose axis")
_expect(seed_config.get("role_seed") == 3302, "seed lock did not reroll role axis")
_expect(row.get("trigger") == "sxcpinup_coloredpencil", "generation profile trigger did not apply")
_expect_trigger_once("config_route_location_theme.prompt", row.get("prompt"), "sxcpinup_coloredpencil")
typed_route = krea_configured_cast_formatter.format_configured_cast_result(
krea_formatter._krea_configured_cast_request_from_row(row, "balanced", "preserve"),
krea_formatter._krea_configured_cast_dependencies(),
)
legacy_route = krea_formatter._normal_row_to_krea(row, "balanced", "preserve")
_expect(
typed_route.as_tuple() == legacy_route,
"Typed Krea configured-cast formatter route should match legacy wrapper output",
)
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
prompt = krea.get("krea_prompt") or ""
_expect("library" in prompt.lower() or "bookshelves" in prompt.lower(), "Krea config route lost theme scene")
_expect("Library camera layout" in prompt, "Krea config route lost library camera-scene directive")
_expect("315-degree front-left quarter view" in prompt, "Krea config route lost camera directive")
_expect_formatter_outputs(row, "config_route_location_theme", target="single")
parking_location_config, parking_composition_config = _thematic_location_configs("parking_garage")
parking_row = pb.build_prompt_from_configs(
row_number=1,
start_index=1,
seed=3311,
category_config=pb.build_category_config_json("woman", "random"),
cast_config=pb.build_cast_config_json("solo_woman", 1, 0),
generation_profile=pb.build_generation_profile_json(profile="balanced"),
camera_config=_orbit_camera(
horizontal_angle=135,
vertical_angle=-30,
zoom=4.0,
subject_focus="environment",
),
location_config=parking_location_config,
composition_config=parking_composition_config,
)
_expect_row_base(parking_row, "config_route_location_theme.parking")
parking_scene = _expect_text("config_route_location_theme.parking_scene", parking_row.get("scene_text"), 20)
parking_composition = _expect_text("config_route_location_theme.parking_composition", parking_row.get("composition"), 10)
parking_directive = _expect_text(
"config_route_location_theme.parking_camera_scene_directive",
parking_row.get("camera_scene_directive"),
40,
)
parking_profile = parking_row.get("scene_camera_profile") if isinstance(parking_row.get("scene_camera_profile"), dict) else {}
_expect("parking" in parking_scene.lower() or "garage" in parking_scene.lower(), "parking theme did not drive scene")
_expect("parking" in parking_composition.lower() or "garage" in parking_composition.lower() or "pillar" in parking_composition.lower(), "parking theme did not drive composition")
_expect(parking_row.get("location_theme") == "parking_garage", "parking location theme did not survive")
_expect(parking_row.get("scene_theme") == "parking_garage", "parking scene theme did not survive")
_expect(parking_row.get("main_category") == "woman", "parking built-in woman preset fell back to another category")
_expect(parking_row.get("primary_subject") == "woman", "parking built-in woman preset did not generate a woman")
_expect(parking_row.get("scene_camera_profile_key") == "parking_garage", "parking theme did not expose camera profile key")
_expect(parking_profile.get("family") == "semi_public", "parking camera profile family should be semi_public")
_expect("Parking garage camera layout" in parking_directive, "parking theme did not drive camera-scene adapter")
_expect("back-right quarter view" in parking_directive, "parking camera-scene adapter missed orbit direction")
_expect("low-angle shot" in parking_directive, "parking camera-scene adapter missed elevation")
creator_location_config, creator_composition_config = _thematic_location_configs("creator_bedroom")
creator_row = pb.build_prompt_from_configs(
row_number=1,
start_index=1,
seed=3321,
category_config=pb.build_category_config_json("woman", "random"),
cast_config=pb.build_cast_config_json("solo_woman", 1, 0),
generation_profile=pb.build_generation_profile_json(profile="balanced"),
camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=0,
zoom=5.5,
subject_focus="environment",
),
location_config=creator_location_config,
composition_config=creator_composition_config,
)
_expect_row_base(creator_row, "config_route_location_theme.creator")
creator_scene = _expect_text("config_route_location_theme.creator_scene", creator_row.get("scene_text"), 20)
creator_composition = _expect_text("config_route_location_theme.creator_composition", creator_row.get("composition"), 10)
creator_directive = _expect_text(
"config_route_location_theme.creator_camera_scene_directive",
creator_row.get("camera_scene_directive"),
40,
)
creator_profile = creator_row.get("scene_camera_profile") if isinstance(creator_row.get("scene_camera_profile"), dict) else {}
_expect("creator" in creator_scene.lower() or "phone" in creator_scene.lower(), "creator theme did not drive scene")
_expect(
any(token in creator_composition.lower() for token in ("creator", "tripod", "phone", "bed")),
"creator theme did not drive composition",
)
_expect(creator_row.get("location_theme") == "creator_bedroom", "creator location theme did not survive")
_expect(creator_row.get("scene_theme") == "creator_bedroom", "creator scene theme did not survive")
_expect(creator_row.get("main_category") == "woman", "creator built-in woman preset fell back to another category")
_expect(creator_row.get("primary_subject") == "woman", "creator built-in woman preset did not generate a woman")
_expect(creator_row.get("scene_camera_profile_key") == "creator_bedroom", "creator theme did not expose camera profile key")
_expect(creator_profile.get("family") == "private_creator", "creator camera profile family should be private_creator")
_expect("Creator room camera layout" in creator_directive, "creator theme did not drive camera-scene adapter")
_expect("front-right quarter view" in creator_directive, "creator camera-scene adapter missed orbit direction")
creator_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(creator_row), target="single")
creator_prompt = creator_krea.get("krea_prompt") or ""
_expect("Creator room camera layout" in creator_prompt, "Krea config route lost creator camera-scene directive")
def smoke_builder_prompt_route_policy() -> None:
def legacy_from_request(request: builder_prompt_route.PromptBuildRequest) -> dict[str, Any]:
return pb.build_prompt(
category=request.category,
subcategory=request.subcategory,
row_number=request.row_number,
start_index=request.start_index,
seed=request.seed,
clothing=request.clothing,
ethnicity=request.ethnicity,
poses=request.poses,
backside_bias=request.backside_bias,
figure=request.figure,
no_plus_women=request.no_plus_women,
no_black=request.no_black,
minimal_clothing_ratio=request.minimal_clothing_ratio,
standard_pose_ratio=request.standard_pose_ratio,
trigger=request.trigger,
prepend_trigger_to_prompt=request.prepend_trigger_to_prompt,
extra_positive=request.extra_positive,
extra_negative=request.extra_negative,
seed_config=request.seed_config,
women_count=request.women_count,
men_count=request.men_count,
camera_config=request.camera_config,
expression_intensity=request.expression_intensity,
character_profile=request.character_profile,
character_cast=request.character_cast,
expression_enabled=request.expression_enabled,
expression_phase=request.expression_phase,
hardcore_position_config=request.hardcore_position_config,
location_config=request.location_config,
composition_config=request.composition_config,
)
seed_config_json = pb.build_seed_lock_config_json(base_seed=3501, reroll_axis="content", reroll_seed=3502)
request = builder_prompt_route.PromptBuildRequest(
category="Casual clothes",
subcategory="Casual clothes / Smart casual",
row_number=3,
start_index=8,
seed=3501,
clothing="random",
ethnicity="french_european",
poses="random",
backside_bias=0.2,
figure="random",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=0.3,
standard_pose_ratio=0.4,
trigger="sxcpinup_coloredpencil",
prepend_trigger_to_prompt=True,
extra_positive="typed builder route marker",
extra_negative="typed builder negative marker",
seed_config=seed_config_json,
women_count=1,
men_count=0,
camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
expression_intensity=0.6,
expression_enabled=True,
)
typed_route = builder_prompt_route.build_prompt_result(request, pb._prompt_build_dependencies())
legacy_row = legacy_from_request(request)
_expect(typed_route.row == legacy_row, "Typed builder prompt route should match public wrapper output")
_expect(typed_route.category == "Casual clothes", "Builder prompt route changed category")
_expect(typed_route.subcategory == "Casual clothes / Smart casual", "Builder prompt route changed subcategory")
_expect(typed_route.branch == "custom", "Builder prompt route should use custom branch for category JSON route")
_expect(typed_route.parsed_seed_config.get("content_seed") == 3502, "Builder prompt route lost seed config")
custom_trace = typed_route.row.get("generation_trace")
_expect(isinstance(custom_trace, dict), "Builder custom route lost generation_trace")
_expect(custom_trace.get("branch") == "custom", "Builder custom generation_trace lost branch")
_expect(custom_trace.get("source") == "json_category", "Builder custom generation_trace lost source")
_expect(custom_trace.get("category_slug") == "casual_clothes", "Builder custom generation_trace lost category slug")
_expect(custom_trace.get("content_seed_axis") == "content", "Builder custom generation_trace lost content axis")
_expect(custom_trace.get("seed_axes", {}).get("content", {}).get("source") == "configured", "Builder custom generation_trace lost configured content seed")
_expect(custom_trace.get("seed_axes", {}).get("content", {}).get("seed") == 3502, "Builder custom generation_trace lost content seed value")
_expect("typed builder route marker" in typed_route.row.get("prompt", ""), "Builder prompt route lost extra positive")
_expect("typed builder negative marker" in typed_route.row.get("negative_prompt", ""), "Builder prompt route lost extra negative")
_expect(
"45-degree front-right quarter view" in typed_route.row.get("camera_directive", ""),
"Builder prompt route lost camera config",
)
_expect_trigger_once("builder_prompt_route_policy.prompt", typed_route.row.get("prompt"), "sxcpinup_coloredpencil")
built_in_request = builder_prompt_route.PromptBuildRequest(
category="woman",
subcategory="random",
row_number=1,
start_index=1,
seed=3503,
clothing="full",
ethnicity="any",
poses="standard",
backside_bias=0.0,
figure="curvy",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=0.0,
standard_pose_ratio=1.0,
trigger=Trigger,
prepend_trigger_to_prompt=True,
extra_positive="built-in route marker",
extra_negative="built-in route negative",
expression_intensity=0.5,
expression_enabled=False,
)
built_in_route = builder_prompt_route.build_prompt_result(built_in_request, pb._prompt_build_dependencies())
_expect(built_in_route.row == legacy_from_request(built_in_request), "Builder built-in route should match public wrapper")
_expect(built_in_route.branch == "built_in", "Builder prompt route lost built-in branch")
_expect(built_in_route.row.get("source") == "built_in_generator", "Builder built-in branch changed source")
built_in_trace = built_in_route.row.get("generation_trace")
_expect(isinstance(built_in_trace, dict), "Builder built-in route lost generation_trace")
_expect(built_in_trace.get("branch") == "built_in", "Builder built-in generation_trace lost branch")
_expect(built_in_trace.get("source") == "built_in_generator", "Builder built-in generation_trace lost source")
_expect(built_in_trace.get("seed_axes", {}).get("person", {}).get("source") == "main", "Builder built-in generation_trace should follow main seed")
_expect(built_in_route.row.get("expression_disabled") is True, "Builder built-in branch lost expression disable")
_expect("built-in route marker" in built_in_route.row.get("prompt", ""), "Builder built-in branch lost extra positive")
auto_weighted_request = builder_prompt_route.PromptBuildRequest(
category="auto_weighted",
subcategory="random",
row_number=2,
start_index=10,
seed=3504,
clothing="random",
ethnicity="any",
poses="random",
backside_bias=0.35,
figure="random",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=0.4,
standard_pose_ratio=0.6,
trigger=Trigger,
prepend_trigger_to_prompt=True,
extra_positive="auto route marker",
extra_negative="auto route negative",
seed_config=pb.build_seed_lock_config_json(base_seed=3504, reroll_axis="person", reroll_seed=3505),
expression_intensity=0.7,
expression_enabled=True,
)
auto_route = builder_prompt_route.build_prompt_result(auto_weighted_request, pb._prompt_build_dependencies())
_expect(auto_route.row == legacy_from_request(auto_weighted_request), "Builder auto-weighted route should match public wrapper")
_expect(auto_route.branch == "auto_weighted", "Builder prompt route lost auto-weighted branch")
_expect(auto_route.parsed_seed_config.get("person_seed") == 3505, "Builder auto-weighted branch lost person seed lock")
auto_trace = auto_route.row.get("generation_trace")
_expect(isinstance(auto_trace, dict), "Builder auto-weighted route lost generation_trace")
_expect(auto_trace.get("branch") == "auto_weighted", "Builder auto-weighted generation_trace lost branch")
_expect(auto_trace.get("seed_axes", {}).get("person", {}).get("source") == "configured", "Builder auto-weighted trace lost configured person seed")
_expect(auto_trace.get("seed_axes", {}).get("person", {}).get("seed") == 3505, "Builder auto-weighted trace lost person seed")
_expect("auto route marker" in auto_route.row.get("prompt", ""), "Builder auto-weighted branch lost extra positive")
def smoke_builder_config_route_policy() -> None:
category_config = pb.build_category_config_json("women_casual", "Casual clothes / Smart casual")
cast_config = pb.build_cast_config_json("solo_woman")
generation_profile = pb.build_generation_profile_json(
profile="casual_clean",
trigger_policy="prepend_trigger",
)
filter_config = pb.build_filter_config_json(
ethnicity="french_european",
figure="balanced",
)
seed_config_json = pb.build_seed_lock_config_json(base_seed=3401, reroll_axis="scene", reroll_seed=3402)
request = builder_config_route.PromptFromConfigsRequest(
row_number=2,
start_index=5,
seed=3401,
category_config=category_config,
cast_config=cast_config,
generation_profile=generation_profile,
filter_config=filter_config,
seed_config=seed_config_json,
extra_positive="clean route marker",
extra_negative="bad route marker",
)
typed_route = builder_config_route.build_prompt_from_configs_result(
request,
pb._prompt_from_configs_dependencies(),
)
legacy_row = pb.build_prompt_from_configs(
row_number=request.row_number,
start_index=request.start_index,
seed=request.seed,
category_config=category_config,
cast_config=cast_config,
generation_profile=generation_profile,
filter_config=filter_config,
seed_config=seed_config_json,
extra_positive=request.extra_positive,
extra_negative=request.extra_negative,
)
_expect(typed_route.row == legacy_row, "Prompt Builder From Configs route should match public wrapper output")
_expect(typed_route.category == "Casual clothes", "Config route lost category preset")
_expect(typed_route.subcategory == "Casual clothes / Smart casual", "Config route lost requested subcategory")
_expect(typed_route.cast["women_count"] == 1 and typed_route.cast["men_count"] == 0, "Config route lost cast preset")
_expect(typed_route.profile["trigger"] == "sxcpinup_coloredpencil", "Config route lost generation profile trigger")
_expect(typed_route.filters["ethnicity"] == "french_european", "Config route lost filter ethnicity")
config_trace = typed_route.row.get("generation_trace")
_expect(isinstance(config_trace, dict), "Config route row lost generation_trace")
_expect(config_trace.get("branch") == "custom", "Config route generation_trace lost builder branch")
_expect(config_trace.get("seed_axes", {}).get("scene", {}).get("source") == "configured", "Config route generation_trace lost scene seed lock")
_expect(config_trace.get("seed_axes", {}).get("scene", {}).get("seed") == 3402, "Config route generation_trace lost scene reroll seed")
kwargs = typed_route.build_kwargs
_expect(kwargs["category"] == typed_route.category, "Config route build kwargs category drifted")
_expect(kwargs["subcategory"] == typed_route.subcategory, "Config route build kwargs subcategory drifted")
_expect(kwargs["women_count"] == 1 and kwargs["men_count"] == 0, "Config route build kwargs cast counts drifted")
_expect(kwargs["seed_config"] == seed_config_json, "Config route build kwargs seed config drifted")
_expect(kwargs["extra_positive"] == "clean route marker", "Config route build kwargs extra positive drifted")
_expect("clean route marker" in typed_route.row.get("prompt", ""), "Config route row lost extra positive")
_expect("bad route marker" in typed_route.row.get("negative_prompt", ""), "Config route row lost extra negative")
def smoke_krea_normal_row_routes() -> None:
single = {
"subject_type": "woman",
"primary_subject": "woman",
"age_band": "25-year-old adult",
"body_phrase": "slim figure",
"skin": "fair skin",
"hair": "long blonde hair",
"eyes": "blue eyes",
"item": "silk dress",
"pose": "standing beside a window",
"scene_text": "quiet studio with warm daylight",
"camera_scene_directive": "Camera-aware studio layout with window depth.",
"expression": "soft smile",
"composition": "vertical centered portrait",
"camera_directive": "Camera: eye-level medium shot",
"style": "realistic creator-shot photography",
}
_expect_krea_normal_route_parity(single, "krea_normal_single", "metadata(single)")
single_figure_note = dict(
single,
body_phrase="slim busty figure with soft, natural fullness up top and a small waist",
)
figure_note_prompt, figure_note_method = krea_formatter._normal_row_to_krea(
single_figure_note,
"balanced",
"preserve",
)
_expect(figure_note_method == "metadata(single)", "Krea single figure-note route changed method")
_expect(
"adult woman, with slim busty figure with" not in figure_note_prompt,
"Krea single route kept old comma-with figure grammar",
)
_expect(
"adult woman with a slim busty figure defined by soft" in figure_note_prompt,
"Krea single route did not attach figure-note appearance to the subject",
)
_expect(
"adult woman with a slim busty figure with" not in figure_note_prompt,
"Krea single route kept nested figure-with wording",
)
_expect(
"adult woman, showing slim busty figure with" not in figure_note_prompt,
"Krea single route kept awkward showing figure-note grammar",
)
_expect(
"warm daylight, Camera-aware studio layout" not in figure_note_prompt,
"Krea single route joined camera-scene directive to scene with a comma",
)
_expect(
"warm daylight. Camera-aware studio layout" in figure_note_prompt,
"Krea single route did not split camera-scene directive into its own sentence",
)
style_metadata = dict(
single,
style="metadata style phrase",
positive_suffix="metadata suffix phrase",
prompt="Use: stale prompt suffix phrase.",
)
style_prompt, style_method = krea_formatter._normal_row_to_krea(style_metadata, "balanced", "preserve")
_expect(style_method == "metadata(single)", "Krea style metadata route changed method")
style_prompt_lower = style_prompt.lower()
_expect("metadata style phrase" in style_prompt_lower, "Krea metadata route lost structured style")
_expect("metadata suffix phrase" in style_prompt_lower, "Krea metadata route lost structured positive suffix")
_expect("stale prompt suffix" not in style_prompt_lower, "Krea metadata route parsed stale Use prompt text")
couple = {
"subject_type": "couple",
"primary_subject": "a woman and a man",
"subject_phrase": "woman and man",
"age": "25-year-old adult and 40-year-old adult",
"body": "slim and average builds",
"item": "Partner A wears black dress; Partner B wears dark shirt",
"pose": "standing close together",
"scene_text": "private lounge with soft lamps",
"expression": "shared confident gaze",
"composition": "two-person editorial frame",
"camera_directive": "Camera: front view, medium shot",
"style": "realistic social photo",
}
_expect_krea_normal_route_parity(couple, "krea_normal_couple", "metadata(couple)")
generic = {
"subject_type": "location",
"primary_subject": "adult editorial scene",
"item": "polished lounge styling",
"scene_text": "hotel hallway with warm wall sconces",
"expression": "quiet atmosphere",
"composition": "wide establishing frame",
"camera_directive": "Camera: wide shot",
"style": "clean photographic realism",
}
_expect_krea_normal_route_parity(generic, "krea_normal_generic", "metadata(generic)")
configured_with_descriptor = _fixture_hardcore_row(
prompt="Characters: stale prompt subject, stale body, stale skin, stale hair, stale eyes.",
cast_descriptor_text=(
"Woman A: 30-year-old adult woman, toned figure, fair skin, red hair, gray eyes; "
"Man A: 45-year-old adult man, average figure, tan skin, dark hair"
),
)
descriptor_prompt, descriptor_method = krea_formatter._normal_row_to_krea(
configured_with_descriptor,
"balanced",
"preserve",
)
_expect(descriptor_method == "metadata(configured_cast)", "Krea configured-cast route changed method")
_expect("30-year-old adult woman" in descriptor_prompt, "Krea configured-cast route lost descriptor metadata")
_expect("toned figure" in descriptor_prompt, "Krea configured-cast route lost descriptor body metadata")
_expect("stale" not in descriptor_prompt, "Krea configured-cast route parsed stale prompt character labels")
configured_without_descriptor = _fixture_hardcore_row(
prompt="Characters: stale prompt subject, stale body, stale skin, stale hair, stale eyes.",
cast_descriptor_text="",
cast_descriptors=[],
)
no_descriptor_prompt, no_descriptor_method = krea_formatter._normal_row_to_krea(
configured_without_descriptor,
"balanced",
"preserve",
)
_expect(no_descriptor_method == "metadata(configured_cast)", "Krea configured-cast no-descriptor route changed method")
_expect("stale" not in no_descriptor_prompt, "Krea configured-cast route should not parse prompt labels without metadata")
_expect("private studio room with warm light" in no_descriptor_prompt, "Krea configured-cast route lost structured scene")
def smoke_krea_action_details_policy() -> None:
_expect(
krea_action_details.strip_redundant_position_detail(
"kneeling penis-licking position while slow tongue licking on the underside of the penis"
)
== "slow tongue licking on the underside of the penis",
"Krea action detail cleanup should remove leading position-while scaffolding",
)
_expect(
krea_action_details.strip_redundant_position_detail(
"raised edge fingering position featuring mutual masturbation with both bodies touching themselves"
)
== "mutual masturbation with both bodies touching themselves",
"Krea action detail cleanup should remove leading position-featuring scaffolding",
)
_expect(
krea_action_details.strip_redundant_position_detail(
"footjob with toes curled around the penis shaft in seated footjob position"
)
== "footjob with toes curled around the penis shaft",
"Krea action detail cleanup should remove trailing in-position scaffolding",
)
_expect(
"position while"
not in krea_action_details.dedupe_outercourse_detail(
"kneeling penis-licking position while slow tongue licking on the underside of the penis",
"the woman bends forward between the man's open thighs",
"penis licking",
{"position": "kneeling penis-licking position"},
).lower(),
"Krea outercourse detail cleanup leaked position-while scaffolding",
)
_expect(
"position featuring"
not in krea_action_details.sanitize_foreplay_detail(
"raised edge fingering position featuring mutual masturbation with both bodies touching themselves",
"the woman and man sit close facing each other",
).lower(),
"Krea foreplay/manual detail cleanup leaked position-featuring scaffolding",
)
_expect(
krea_action_details.dedupe_anchor_detail(
"side-lying anal position, one leg lifted high",
"side-lying rear-entry anal pose",
)
== "one leg lifted high",
"Krea anchored detail cleanup should remove repeated anal position prefix",
)
def smoke_outercourse_action_policy() -> None:
_expect(
outercourse_action_policy.infer_outercourse_action_kind("kneeling boobjob position")
== outercourse_action_policy.OUTERCOURSE_BOOBJOB,
"Outercourse classifier lost boobjob position detection",
)
_expect(
outercourse_action_policy.infer_outercourse_action_kind("bent-over balls-licking position")
== outercourse_action_policy.OUTERCOURSE_TESTICLE,
"Outercourse classifier lost testicle/balls position detection",
)
_expect(
outercourse_action_policy.infer_outercourse_action_kind("tongue runs along the underside of the penis")
== outercourse_action_policy.OUTERCOURSE_PENIS_LICKING,
"Outercourse classifier lost penis-licking detail detection",
)
_expect(
outercourse_action_policy.infer_outercourse_action_kind("two-handed handjob with thumb and fingers around the penis")
== outercourse_action_policy.OUTERCOURSE_HANDJOB,
"Outercourse classifier lost handjob detection",
)
_expect(
outercourse_action_policy.infer_outercourse_action_kind("footjob with toes curled around the penis")
== outercourse_action_policy.OUTERCOURSE_FOOTJOB,
"Outercourse classifier lost footjob detection",
)
axis_text = krea_action_context.axis_values_text(
{
"outer_act": "handjob with one hand wrapped around the penis",
"contact_detail": "hand wrapped around the penis shaft with the glans visible",
"visibility": "hand and penis centered in frame",
}
)
_expect("handjob" in axis_text and "hand and penis" in axis_text, "Krea action context lost outercourse axes")
role_graph = hardcore_role_outercourse.build_outercourse_role_graph(
"Woman A",
"Man A",
"testicle sucking with lips around the balls",
{"position": "bent-over testicle-sucking position"},
["Man A"],
)
lower_role = role_graph.lower()
_expect("face below the pov viewer's penis at testicle height" in lower_role, "POV testicle role graph lost low-head geometry")
_expect("penis points upward" in lower_role, "POV testicle role graph lost foreground penis geometry")
deduped = krea_action_details.dedupe_outercourse_detail(
"testicle sucking with lips around the balls, balls and mouth contact visible, wet lips and tongue contact",
role_graph,
"testicle sucking with lips around the balls",
{"position": "bent-over testicle-sucking position"},
).lower()
_expect("testicle" not in deduped and "balls and mouth" not in deduped, "Krea outercourse dedupe kept redundant testicle clauses")
_expect("wet lips" in deduped, "Krea outercourse dedupe removed useful texture clause")
def smoke_item_axis_policy() -> None:
axis_values = {
"ignored": "random",
"position": "kneeling oral position",
"contact_detail": {"text": "mouth contact at hip height"},
"nested": {"unused": "fallback body detail"},
"list_detail": ["hands on hips", "auto"],
"unprioritized_detail": "extra unprioritized cue",
}
texts = item_axis_policy.axis_value_texts(axis_values)
_expect("kneeling oral position" in texts, "Item axis policy lost position value")
_expect("mouth contact at hip height" in texts, "Item axis policy lost preferred dict text")
_expect("fallback body detail" in texts, "Item axis policy lost nested fallback text")
_expect("hands on hips" in texts, "Item axis policy lost list text")
_expect("random" not in texts and "auto" not in texts, "Item axis policy leaked placeholder values")
_expect(
item_axis_policy.axis_value_texts(axis_values, existing_text="kneeling oral position already present")[0]
== "mouth contact at hip height",
"Item axis policy should skip details already present in existing text",
)
context_text = item_axis_policy.action_context_text(axis_values)
_expect("kneeling oral position" in context_text, "Item axis policy context lost priority position")
_expect("mouth contact at hip height" in context_text, "Item axis policy context lost priority contact")
_expect("extra unprioritized cue" not in context_text, "Item axis policy context should ignore unprioritized values")
_expect(
krea_action_context.axis_values_text(axis_values) == context_text,
"Krea action context should delegate to shared item axis policy",
)
def smoke_krea_row_fields_policy() -> None:
row = {
"subject_type": "configured_cast",
"primary_subject": "woman",
"cast_summary": "1 woman, 1 man",
"item": "lace bodysuit fashion editorial styling",
"pose": "standing close together",
"scene_text": "private room with warm lamps",
"expression": "soft smile",
"expression_enabled": False,
"composition": "vertical tight two-person frame",
"source_composition": "vertical source action frame",
"camera_directive": "Camera: eye-level close-up",
"camera_scene_directive": "Camera-aware scene layout.",
"style": "realistic social photo",
}
fields = krea_row_fields.extract_krea_row_fields(
row,
"preserve",
krea_formatter._krea_row_field_dependencies(),
)
normal_request = krea_formatter._krea_normal_row_request_from_row(row, "balanced", "preserve")
cast_request = krea_formatter._krea_configured_cast_request_from_row(row, "balanced", "preserve")
_expect(fields.item == "lace bodysuit", "Krea row fields did not strip generic styling suffix")
_expect(fields.expression == "", "Krea row fields ignored expression disabled flag")
_expect(fields.composition == "tight two-person frame", "Krea row fields did not normalize composition prefix")
_expect(fields.source_composition == "source action frame", "Krea row fields did not normalize source composition")
_expect(normal_request.item == fields.item, "Normal Krea route item extraction drifted")
_expect(cast_request.item == fields.item, "Configured-cast Krea route item extraction drifted")
_expect(normal_request.expression == cast_request.expression == fields.expression, "Krea route expression extraction drifted")
_expect(normal_request.camera == cast_request.camera == fields.camera, "Krea route camera extraction drifted")
_expect(cast_request.source_composition == fields.source_composition, "Configured-cast source composition drifted")
def smoke_location_config_policy() -> None:
_expect(pb.LOCATION_POOL_PRESETS is location_config.LOCATION_POOL_PRESETS, "Prompt builder location presets are not delegated")
_expect(pb.COMPOSITION_POOL_PRESETS is location_config.COMPOSITION_POOL_PRESETS, "Prompt builder composition presets are not delegated")
_expect("classical_library" in location_config.location_theme_choices(), "Location themes lost classical_library")
_expect("creator_bedroom" in location_config.location_theme_choices(), "Location themes lost creator_bedroom")
_expect("mirror_room" in location_config.location_theme_choices(), "Location themes lost mirror_room")
_expect("workspace_lounge" in location_config.location_theme_choices(), "Location themes lost workspace_lounge")
_expect("fetish_studio" in location_config.location_theme_choices(), "Location themes lost fetish_studio")
custom = json.loads(
pb.build_location_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_locations="custom_room: a quiet room with warm lamps",
)
)
_expect(custom.get("enabled") is True, "Custom location config should be active")
_expect(custom.get("apply_mode") == "replace", "Custom location config lost replace mode")
_expect(custom.get("scene_entries", [{}])[0].get("slug") == "custom_room", "Custom location slug parser changed")
structured_custom = json.loads(
pb.build_location_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_locations=json.dumps(
{
"slug": "structured_room",
"text": "structured room with preserved metadata",
"camera_profile": {
"key": "structured_camera_profile",
"foreground": "foreground test anchor",
"midground": "middle test anchor",
"background": "background test anchor",
},
},
sort_keys=True,
),
)
)
structured_entry = structured_custom.get("scene_entries", [{}])[0]
_expect(structured_entry.get("slug") == "structured_room", "Structured custom location lost slug")
_expect(structured_entry.get("prompt") == "structured room with preserved metadata", "Structured custom location did not normalize prompt text")
_expect(structured_entry.get("camera_profile", {}).get("key") == "structured_camera_profile", "Structured custom location lost camera profile metadata")
added = json.loads(
location_config.build_location_pool_json(
enabled=True,
combine_mode="add",
preset="custom_only",
custom_locations="second_room: another quiet room",
location_config=custom,
)
)
_expect(added.get("apply_mode") == "replace", "Location add merge should preserve incoming apply_mode")
_expect(len(added.get("scene_entries") or []) == 2, "Location add merge did not keep both custom locations")
composition = json.loads(
pb.build_composition_pool_json(
enabled=True,
combine_mode="replace",
preset="no_outfit_check",
custom_compositions="manual frame through foreground bookshelves",
)
)
_expect(composition.get("enabled") is True, "Composition config should be active")
_expect(
any("outfit-check" in str(entry) for entry in composition.get("composition_entries") or []),
"Composition inline preset no_outfit_check was not applied",
)
structured_composition = json.loads(
pb.build_composition_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_compositions=json.dumps({"text": "structured composition frame", "source": "json_line"}, sort_keys=True),
)
)
structured_composition_entry = structured_composition.get("composition_entries", [{}])[0]
_expect(structured_composition_entry.get("prompt") == "structured composition frame", "Structured custom composition did not normalize prompt text")
_expect(structured_composition_entry.get("source") == "json_line", "Structured custom composition lost metadata")
parsed = pb._parse_location_config({"enabled": True, "pool_names": [], "scene_entries": custom["scene_entries"]})
_expect(pb._location_config_active(parsed), "Prompt builder location parser wrapper is inactive")
themed_location, themed_composition, theme_summary = pb.build_thematic_location_json(
enabled=True,
combine_mode="replace",
theme="classical_library",
)
_expect("classical_library" in theme_summary, "Themed location summary lost theme name")
themed_location_payload = json.loads(themed_location)
themed_composition_payload = json.loads(themed_composition)
_expect(themed_location_payload.get("scene_entries"), "Themed location did not output locations")
_expect(themed_location_payload.get("theme") == "classical_library", "Themed location config lost theme metadata")
_expect(
all(
not isinstance(entry, dict) or entry.get("theme") == "classical_library"
for entry in themed_location_payload.get("scene_entries") or []
),
"Themed location entries lost theme metadata",
)
_expect(themed_composition_payload.get("composition_entries"), "Themed location did not output compositions")
_expect(themed_composition_payload.get("theme") == "classical_library", "Themed composition config lost theme metadata")
parsed_themed = pb._parse_location_config(themed_location_payload)
_expect(parsed_themed.get("theme") == "classical_library", "Location parser lost theme metadata")
workspace_location, workspace_composition, workspace_summary = pb.build_thematic_location_json(
enabled=True,
combine_mode="replace",
theme="workspace_lounge",
)
workspace_location_payload = json.loads(workspace_location)
workspace_composition_payload = json.loads(workspace_composition)
_expect("workspace_lounge" in workspace_summary, "Workspace theme summary lost theme name")
_expect(
workspace_location_payload.get("scene_entries", [{}])[0].get("slug") == "coworking_lounge_window",
"Workspace theme lost coworking lounge location slug",
)
_expect(workspace_composition_payload.get("composition_entries"), "Workspace theme did not output compositions")
workspace_compositions_text = " ".join(str(entry) for entry in workspace_composition_payload.get("composition_entries") or [])
_expect("camera-aware" not in workspace_compositions_text.lower(), "Workspace theme leaked internal camera-aware wording")
replaced_after_theme = json.loads(
location_config.build_location_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_locations="plain_room: plain room after theme",
location_config=themed_location_payload,
)
)
_expect(replaced_after_theme.get("theme") == "", "Location replace mode should not inherit upstream theme metadata")
replaced_composition_after_theme = json.loads(
location_config.build_composition_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_compositions="plain composition after theme",
composition_config=themed_composition_payload,
)
)
_expect(
replaced_composition_after_theme.get("theme") == "",
"Composition replace mode should not inherit upstream theme metadata",
)
def smoke_row_location_policy() -> None:
location = json.loads(
location_config.build_location_pool_json(
combine_mode="replace",
custom_locations="archive_corner: hidden archive corner with repeated shelves and warm table lamps",
)
)
composition = json.loads(
location_config.build_composition_pool_json(
combine_mode="replace",
custom_compositions="long archive aisle composition",
)
)
row = {
"source": "built_in_generator",
"primary_subject": "adult woman",
"scene": "unknown_old_scene",
"composition": "old frame",
"prompt": "A generated adult prompt. Scene: old room. Pose: standing. Composition: vertical old frame. Avoid: low quality.",
"caption": "sxcppnl7, generated adult prompt, old room, old frame, illustration",
}
updated = row_location.apply_location_config_to_legacy_row(dict(row), location, {}, 123, 1)
updated = row_location.apply_composition_config_to_legacy_row(updated, composition, {}, 123, 1)
_expect(updated.get("scene") == "archive_corner", "Row location policy did not select forced custom scene slug")
_expect(
updated.get("scene_text") == "hidden archive corner with repeated shelves and warm table lamps",
"Row location policy did not apply forced custom scene text",
)
_expect(updated.get("source_scene") == "unknown_old_scene", "Row location policy lost source scene slug")
_expect(updated.get("scene_entry", {}).get("slug") == "archive_corner", "Row location policy lost selected scene entry")
_expect(
"Scene: hidden archive corner with repeated shelves and warm table lamps. Pose:" in updated.get("prompt", ""),
"Row location policy did not rewrite prompt scene",
)
_expect(updated.get("composition") == "long archive aisle composition", "Row location policy did not apply forced composition")
_expect(
updated.get("composition_entry", {}).get("prompt") == "long archive aisle composition",
"Row location policy lost selected composition entry",
)
_expect(
updated.get("composition_prompt") == "vertical long archive aisle composition",
"Row location policy did not compute composition prompt",
)
_expect(
"Composition: vertical long archive aisle composition." in updated.get("prompt", ""),
"Row location policy did not rewrite prompt composition",
)
_expect(", long archive aisle composition," in updated.get("caption", ""), "Row location policy did not rewrite caption composition")
def smoke_row_expression_policy() -> None:
entries = [
{"text": "quiet calm focus", "weight": 2.0},
{"text": "heated smirk", "weight": 3.0},
{"text": "dazed overwhelmed look", "weight": 1.0},
]
_expect(
pb._expression_entries_for_intensity(entries, 0.25) == row_expression.expression_entries_for_intensity(entries, 0.25),
"Prompt builder expression intensity wrapper should delegate to row_expression",
)
weighted = row_expression.expression_entries_for_intensity(entries, 0.25)
_expect(weighted[0]["weight"] == 8.0, "Row expression low-intensity weighting changed")
_expect(weighted[2]["weight"] == 0.05, "Row expression distant-intensity weighting changed")
row = {
"prompt": "Person with quiet focus, in room. Facial expression: quiet focus. Keep this.",
"caption": "sxcppnl7, person with quiet focus, in room",
"expression": "quiet focus",
"shared_expression": "quiet focus",
"character_expressions": ["Woman A has quiet focus"],
"character_expression_text": "Woman A has quiet focus",
}
_expect(
pb._disable_row_expression(dict(row), "test") == row_expression.disable_row_expression(dict(row), "test"),
"Prompt builder expression disable wrapper should delegate to row_expression",
)
disabled = row_expression.disable_row_expression(dict(row), "test")
_expect(disabled.get("expression_disabled") is True, "Row expression disable did not set disabled metadata")
_expect("quiet focus" not in disabled.get("prompt", ""), "Row expression disable did not strip prompt expression text")
woman_slot = character_slot.normalize_character_slot(
{
"subject_type": "woman",
"label": "A",
"expression_intensity": 0.8,
"softcore_expression_intensity": 0.2,
}
)
pov_man_slot = character_slot.normalize_character_slot(
{
"subject_type": "man",
"label": "A",
"presence_mode": "pov",
"expression_intensity": 1.0,
}
)
label_map = {"Woman A": woman_slot, "Man A": pov_man_slot}
_expect(
pb._cast_expression_intensity_override(0.5, label_map, 1, 1, "softcore")
== row_expression.cast_expression_intensity_override(0.5, label_map, 1, 1, "softcore"),
"Prompt builder cast expression override wrapper should delegate to row_expression",
)
_expect(
row_expression.cast_expression_intensity_override(0.5, label_map, 1, 1, "softcore")
== (0.2, "character_slot:Woman A"),
"Row expression cast override did not prefer visible slot phase intensity",
)
_expect(
pb._resolve_expression_route(
expression_enabled=True,
expression_intensity=0.5,
expression_intensity_source="input",
subject_type="woman",
applied_slot=woman_slot,
women_count=1,
men_count=0,
expression_phase="softcore",
)
== row_expression.resolve_expression_route(
expression_enabled=True,
expression_intensity=0.5,
expression_intensity_source="input",
subject_type="woman",
applied_slot=woman_slot,
women_count=1,
men_count=0,
expression_phase="softcore",
),
"Prompt builder expression route wrapper should delegate to row_expression",
)
route = row_expression.resolve_expression_route(
expression_enabled=True,
expression_intensity=0.5,
expression_intensity_source="input",
subject_type="woman",
applied_slot=woman_slot,
women_count=1,
men_count=0,
expression_phase="softcore",
)
_expect(route.expression_intensity == 0.2, "Expression route did not apply phase-specific slot intensity")
_expect(route.expression_intensity_source == "character_slot:Woman A", "Expression route lost slot source")
_expect(
pb._character_expression_entries(random.Random(22), entries, 0.5, label_map, 1, 1, "softcore")
== row_expression.character_expression_entries(random.Random(22), entries, 0.5, label_map, 1, 1, "softcore"),
"Prompt builder character expression wrapper should delegate to row_expression",
)
disabled_slot = character_slot.normalize_character_slot(
{"subject_type": "woman", "label": "A", "expression_enabled": False}
)
_expect(
row_expression.cast_expression_intensity_override(0.5, {"Woman A": disabled_slot}, 1, 0, "hardcore")
== (None, "character_slots:disabled"),
"Row expression cast override did not honor all-slot expression disable",
)
global_disabled = row_expression.resolve_expression_route(
expression_enabled=False,
expression_intensity=0.8,
expression_intensity_source="input",
subject_type="woman",
applied_slot=woman_slot,
)
_expect(global_disabled.expression_disabled is True, "Expression route did not honor global disabled state")
_expect(global_disabled.expression_intensity == 0.8, "Expression route changed disabled fallback intensity too early")
_expect(global_disabled.expression_intensity_source == "disabled", "Expression route did not mark global disabled source")
slot_disabled = row_expression.resolve_expression_route(
expression_enabled=True,
expression_intensity=0.5,
expression_intensity_source="input",
subject_type="woman",
applied_slot=disabled_slot,
)
_expect(slot_disabled.expression_disabled is True, "Expression route did not honor single-slot disable")
_expect(
slot_disabled.expression_intensity_source == "character_slot:Woman A:disabled",
"Expression route lost single-slot disabled source",
)
cast_disabled = row_expression.resolve_expression_route(
expression_enabled=True,
expression_intensity=0.5,
expression_intensity_source="input",
subject_type="configured_cast",
character_slots=[disabled_slot],
character_slot_map={"Woman A": disabled_slot},
women_count=1,
men_count=0,
expression_phase="hardcore",
)
_expect(cast_disabled.expression_disabled is True, "Expression route did not honor all-slot cast disable")
_expect(cast_disabled.expression_intensity is None, "Expression route did not clear all-slot disabled intensity")
_expect(
cast_disabled.expression_intensity_source == "character_slots:disabled",
"Expression route lost all-slot disabled source",
)
expression_text = "Woman A has steady focus; Man A has parted lips with saliva"
context_role = "Woman A performs a handjob while Man A stands close"
_expect(
pb._sanitize_character_expression_text_for_action(expression_text, context_role, "", {})
== row_expression.sanitize_character_expression_text_for_action(expression_text, context_role, "", {})
== "Woman A has steady focus",
"Row expression action sanitizer did not remove incompatible partner expression",
)
reverse_context = "Man A has mouth pressed to her pussy while Woman A lies back"
_expect(
row_expression.sanitize_character_expression_text_for_action(
"Woman A has tongue out; Man A has focused mouth contact",
reverse_context,
"",
{},
)
== "Man A has focused mouth contact",
"Row expression action sanitizer did not remove incompatible visible-subject expression",
)
def smoke_row_prompt_axes_policy() -> None:
category = {
"name": "Axis Test",
"slug": "axis_test",
"scenes": [{"slug": "studio", "prompt": "quiet studio with repeatable anchors"}],
"poses": ["standing fallback pose"],
"expressions": ["quiet focus", "heated smirk"],
"compositions": ["all participants visible centered frame"],
}
subcategory = {"name": "Axis Sub", "slug": "axis_sub", "items": ["axis item"]}
item = "axis item"
base_kwargs = {
"category": category,
"subcategory": subcategory,
"item": item,
"subject_type": "woman",
"context": {"fallback_pose": ""},
"poses": "standard",
"women_count": 1,
"men_count": 0,
"expression_disabled": True,
"expression_intensity": 0.5,
"is_pose_category": False,
"location_config": {},
"composition_config": {},
}
route = row_prompt_axes.resolve_prompt_axes(
**base_kwargs,
scene_rng=random.Random(1),
pose_rng=random.Random(2),
expression_rng=random.Random(3),
composition_rng=random.Random(4),
)
route_result = row_prompt_axes.resolve_prompt_axes_result(
**base_kwargs,
scene_rng=random.Random(1),
pose_rng=random.Random(2),
expression_rng=random.Random(3),
composition_rng=random.Random(4),
)
delegated = pb._prompt_axes_route(
**base_kwargs,
scene_rng=random.Random(1),
pose_rng=random.Random(2),
expression_rng=random.Random(3),
composition_rng=random.Random(4),
)
typed_delegated = pb._prompt_axes_route_result(
**base_kwargs,
scene_rng=random.Random(1),
pose_rng=random.Random(2),
expression_rng=random.Random(3),
composition_rng=random.Random(4),
)
_expect(delegated == route, "Prompt builder prompt-axes route should delegate to row_prompt_axes")
_expect(route_result.as_dict() == route, "Typed prompt axes route should match legacy dict route")
_expect(typed_delegated == route_result, "Prompt builder typed prompt-axes route should delegate")
_expect(route_result.scene_slug == "studio", "Typed prompt axes route lost selected scene slug")
_expect(route["scene_slug"] == "studio", "Prompt axes route lost selected scene slug")
_expect(route["scene"] == "quiet studio with repeatable anchors", "Prompt axes route lost selected scene text")
_expect(route["scene_entry"].get("slug") == "studio", "Prompt axes route lost selected scene entry slug")
_expect(
route["scene_entry"].get("prompt") == "quiet studio with repeatable anchors",
"Prompt axes route lost selected scene entry prompt",
)
_expect(route["pose"] == "standing fallback pose", "Prompt axes route lost selected fallback pose")
_expect(route["expression"] == "", "Prompt axes route should omit expression when disabled")
_expect(route["shared_expression"] == "", "Prompt axes route should omit shared expression when disabled")
_expect(route["source_composition"] == "all participants visible centered frame", "Prompt axes route lost source composition")
_expect(
route["composition_entry"].get("prompt") == "all participants visible centered frame",
"Prompt axes route lost selected composition entry",
)
pov_route = row_prompt_axes.resolve_prompt_axes(
**{**base_kwargs, "expression_disabled": True},
scene_rng=random.Random(1),
pose_rng=random.Random(2),
expression_rng=random.Random(3),
composition_rng=random.Random(4),
pov_character_labels=["Man A"],
)
_expect("first-person POV" in pov_route["composition"], "Prompt axes route did not adapt composition for POV")
woman_slot = character_slot.normalize_character_slot(
{"subject_type": "woman", "label": "A", "expression_intensity": 0.25}
)
configured_route = row_prompt_axes.resolve_prompt_axes(
**{
**base_kwargs,
"subject_type": "configured_cast",
"context": {"subject_type": "configured_cast"},
"expression_disabled": False,
"character_slots": [woman_slot],
"character_slot_map": {"Woman A": woman_slot},
},
scene_rng=random.Random(1),
pose_rng=random.Random(2),
expression_rng=random.Random(3),
composition_rng=random.Random(4),
)
_expect(
configured_route["character_expression_text"].startswith("Woman A has "),
"Prompt axes route lost configured-cast character expression",
)
_expect(
configured_route["expression"] == configured_route["character_expression_text"],
"Prompt axes route did not promote character expression text to row expression",
)
def smoke_row_item_policy() -> None:
weighted_entries = [
{"text": "first", "weight": 0.0},
{"text": "second", "weight": 4.0},
{"text": "third", "weight": 1.0},
]
_expect(
pb._weighted_choice(random.Random(44), weighted_entries) == row_item.weighted_choice(random.Random(44), weighted_entries),
"Prompt builder weighted item choice should delegate to row_item",
)
_expect(
pb._pair_from({"name": "Library Corner", "description": "carved shelves and warm lamps"})
== row_item.pair_from({"name": "Library Corner", "description": "carved shelves and warm lamps"}),
"Prompt builder pair parser should delegate to row_item",
)
oral_values = [
"fellatio with penis in mouth",
"cunnilingus with tongue on pussy",
"sixty-nine mutual oral",
]
_expect(
pb._oral_acts_for_position(oral_values, "kneeling oral position")
== row_item.oral_acts_for_position(oral_values, "kneeling oral position")
== ["fellatio with penis in mouth"],
"Row item oral act filtering changed for kneeling oral",
)
outer_values = [
"titjob with compressed breasts",
"handjob with palm around shaft",
"footjob with soles and toes",
]
_expect(
pb._outercourse_acts_for_position(outer_values, "footjob position")
== row_item.outercourse_acts_for_position(outer_values, "footjob position")
== ["footjob with soles and toes"],
"Row item outercourse act filtering changed for footjob",
)
texture_values = [
"fingers pressing around shaft",
"soles pressing around shaft",
"tongue wet against shaft",
]
_expect(
row_item.outercourse_axis_values_for_position(texture_values, "footjob position", "texture_detail")
== ["soles pressing around shaft"],
"Row item outercourse texture axis should prefer footjob-compatible details",
)
anal_leg_values = [
"standing with legs braced",
"one leg lifted high",
"kneeling with thighs apart",
"knees pressed to chest",
]
_expect(
pb._anal_axis_values_for_position(anal_leg_values, "side-lying anal position", "leg_detail")
== row_item.anal_axis_values_for_position(anal_leg_values, "side-lying anal position", "leg_detail")
== ["one leg lifted high"],
"Row item anal leg-detail filtering changed for side-lying anal",
)
category = {}
subcategory = {
"name": "Oral sex",
"slug": "oral_sex",
"item_templates": [
{
"template": "{oral_act}; {hand_detail}",
"action_family": "oral",
"position_family": "oral",
}
],
"item_axes": {
"position": ["kneeling oral position"],
"oral_act": oral_values,
"hand_detail": ["hands on hips", "spreading thighs"],
},
}
item = "Oral sex"
builder_result = pb._compose_item(random.Random(7), category, subcategory, item, women_count=1, men_count=1)
policy_result = row_item.compose_item(random.Random(7), category, subcategory, item, women_count=1, men_count=1)
_expect(builder_result == policy_result, "Prompt builder compose item should delegate to row_item")
item_text, item_name, axis_values, metadata = policy_result
_expect(item_name == "Oral sex", "Row item compose lost item name")
_expect("fellatio" in item_text, "Row item compose did not apply oral act compatibility filter")
_expect(axis_values.get("hand_detail") == "hands on hips", "Row item compose did not apply oral detail filter")
_expect(metadata.get("action_family") == "oral", "Row item compose lost template metadata")
anal_subcategory = {
"name": "Anal and double penetration",
"slug": "anal_double_penetration",
"item_templates": [
{
"template": "{anal_act} in {position}, with {leg_detail}",
"action_family": "default",
"position_family": "anal",
}
],
"item_axes": {
"position": ["side-lying anal position"],
"anal_act": ["penis entering ass"],
"leg_detail": anal_leg_values,
},
}
anal_text, _anal_name, anal_axis_values, _anal_metadata = row_item.compose_item(
random.Random(5),
{},
anal_subcategory,
"Anal and double penetration",
women_count=1,
men_count=1,
)
_expect("standing with legs braced" not in anal_text, "Row item compose leaked standing legs into side-lying anal")
_expect(anal_axis_values.get("leg_detail") == "one leg lifted high", "Row item compose did not apply anal leg-detail filter")
def smoke_row_category_route_policy() -> None:
hard_config = hardcore_position_config.parse_hardcore_position_config(_position_filter("oral_only", "oral", ["kneeling"]))
seed_cfg = seed_config.parse_seed_config({})
route = row_category_route.select_category_item_route(
category_choice="custom_random",
subcategory_choice="Hardcore sexual poses / Oral sex",
seed_config=seed_cfg,
seed=2301,
row_number=1,
women_count=1,
men_count=1,
hardcore_position_config=hard_config,
)
route_result = row_category_route.select_category_item_route_result(
category_choice="custom_random",
subcategory_choice="Hardcore sexual poses / Oral sex",
seed_config=seed_cfg,
seed=2301,
row_number=1,
women_count=1,
men_count=1,
hardcore_position_config=hard_config,
)
delegated = pb._select_category_item_route(
category_choice="custom_random",
subcategory_choice="Hardcore sexual poses / Oral sex",
seed_config=seed_cfg,
seed=2301,
row_number=1,
women_count=1,
men_count=1,
hardcore_position_config=hard_config,
)
typed_delegated = pb._select_category_item_route_result(
category_choice="custom_random",
subcategory_choice="Hardcore sexual poses / Oral sex",
seed_config=seed_cfg,
seed=2301,
row_number=1,
women_count=1,
men_count=1,
hardcore_position_config=hard_config,
)
_expect(delegated == route, "Prompt builder category/item route should delegate to row_category_route")
_expect(route_result.as_dict() == route, "Typed category/item route should match legacy dict route")
_expect(typed_delegated == route_result, "Prompt builder typed category/item route should delegate")
_expect(route_result.content_axis == "pose", "Typed category/item route lost content seed axis")
_expect(route["category"]["slug"] == "hardcore_sexual_poses", "Row category route selected wrong hardcore category")
_expect(route["subcategory"]["slug"] == "oral_sex", "Row category route selected wrong hardcore subcategory")
_expect(route["content_axis"] == "pose", "Hardcore pose category should use pose seed axis")
_expect(route["is_pose_category"] is True, "Hardcore pose category should be marked as pose content")
_expect(isinstance(route["item_axis_values"], dict), "Row category route lost item axis metadata")
_expect(isinstance(route["formatter_hints"], dict), "Row category route lost formatter hint metadata")
_expect(
pb._is_pose_content_category(route["category"], route["subcategory"])
== row_category_route.is_pose_content_category(route["category"], route["subcategory"]),
"Prompt builder pose-content wrapper should delegate",
)
casual_route = row_category_route.select_category_item_route(
category_choice="custom_random",
subcategory_choice="Casual clothes / Streetwear",
seed_config=seed_cfg,
seed=2301,
row_number=1,
women_count=1,
men_count=0,
hardcore_position_config={},
)
_expect(casual_route["category"]["slug"] == "casual_clothes", "Row category route selected wrong casual category")
_expect(casual_route["content_axis"] == "content", "Non-pose category should use content seed axis")
_expect(casual_route["is_pose_category"] is False, "Non-pose category should not be marked as pose content")
exposed_route = row_category_route.select_category_item_route(
category_choice="custom_random",
subcategory_choice="Provocative erotic clothes / Sheer exposed",
seed_config=seed_cfg,
seed=2302,
row_number=1,
women_count=1,
men_count=0,
hardcore_position_config={},
)
_expect(exposed_route["subcategory"]["slug"] == "sheer_exposed", "Row category route selected wrong exposed category")
_expect(exposed_route["content_axis"] == "content", "Exposed clothing slug should not be treated as pose content")
_expect(exposed_route["is_pose_category"] is False, "Exposed clothing slug should not be marked as pose content")
def smoke_row_generation_policy() -> None:
_expect(pb._ratio_or_none(-1) is None, "Prompt builder ratio helper should treat negative as unset")
_expect(pb._ratio_or_none(1.5) == row_generation.ratio_or_none(1.5) == 1.0, "Row generation ratio clamp changed")
_expect(pb._clamped_float("bad", 0.4) == row_generation.clamped_float("bad", 0.4) == 0.4, "Row generation float default changed")
_expect(
pb._pick_clothing_mode(random.Random(1), "random", None)
== row_generation.pick_clothing_mode(random.Random(1), "random", None),
"Prompt builder clothing mode picker should delegate to row_generation",
)
_expect(
row_generation.pick_pose_mode(random.Random(2), "evocative", 1.0) == "standard",
"Row generation standard pose ratio override changed",
)
_expect(
pb._pick_figure_bias(random.Random(3), "random") == row_generation.pick_figure_bias(random.Random(3), "random"),
"Prompt builder figure picker should delegate to row_generation",
)
_expect(
row_generation.pick_expression_intensity(random.Random(4), -1) == (0.24, "random"),
"Row generation random expression intensity changed",
)
_expect(
pb._pick_expression_intensity(random.Random(4), 2.0) == row_generation.pick_expression_intensity(random.Random(4), 2.0) == (1.0, "input"),
"Prompt builder expression intensity picker should delegate to row_generation",
)
direct_args = dict(
category="woman",
row_number=3,
start_index=41,
clothing="full",
ethnicity="any",
poses="standard",
backside_bias=0.25,
figure="curvy",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=None,
standard_pose_ratio=None,
seed=5050,
)
_expect(
pb._build_direct_builtin_row(**direct_args) == row_generation.build_direct_builtin_row(**direct_args),
"Prompt builder direct built-in row should delegate to row_generation",
)
auto_args = dict(
row_number=2,
start_index=41,
clothing="minimal",
ethnicity="any",
poses="evocative",
backside_bias=0.0,
figure="balanced",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=None,
standard_pose_ratio=None,
seed=6060,
)
auto_row = row_generation.build_auto_weighted_row(**auto_args)
_expect(pb._build_auto_weighted_row(**auto_args) == auto_row, "Prompt builder auto-weighted row should delegate to row_generation")
_expect(auto_row.get("source") == "built_in_generator", "Row generation auto-weighted row lost source metadata")
seed_cfg = seed_config.parse_seed_config({"category_seed": 123})
_expect(
pb._auto_full_choice(seed_cfg, 7070, 1) == row_generation.auto_full_choice(seed_cfg, 7070, 1),
"Prompt builder auto-full choice should delegate to row_generation",
)
def smoke_category_extensions_policy() -> None:
_expect(pb.BUILTIN_CATEGORIES is category_extensions.BUILTIN_CATEGORIES, "Prompt builder built-in categories should come from category_extensions")
targets = category_extensions.extension_targets()
_expect(
pb._extension_targets().keys() == targets.keys(),
"Prompt builder extension targets should delegate to category_extensions",
)
_expect(targets.get("group_scenes", (None, None))[1] is True, "Group scene extension target should expect scene pairs")
sample = [{"slug": "a", "prompt": "one"}]
category_extensions.unique_extend(sample, [{"slug": "a", "prompt": "one"}, {"slug": "b", "prompt": "two"}])
_expect(len(sample) == 2 and sample[1]["slug"] == "b", "Category extension unique_extend changed")
pb.apply_pool_extensions()
category_extensions.apply_pool_extensions()
_expect(pb.category_choices() == category_extensions.category_choices(), "Prompt builder category choices should delegate")
_expect(pb.subcategory_choices() == category_extensions.subcategory_choices(), "Prompt builder subcategory choices should delegate")
_expect("Hardcore sexual poses" in pb.category_choices(), "Category choices lost hardcore JSON category")
_expect(
any(slug == "private_suite_group_party" for slug, _prompt in targets["group_scenes"][0]),
"JSON pool_extensions did not reach legacy group scenes",
)
previous_category_dir = category_library.CATEGORY_DIR
previous_extensions_applied = category_extensions._EXTENSIONS_APPLIED
try:
with tempfile.TemporaryDirectory() as temp_dir:
category_library.CATEGORY_DIR = Path(temp_dir)
category_extensions._EXTENSIONS_APPLIED = False
Path(temp_dir, "custom_category.json").write_text(
json.dumps(
{
"categories": {
"Dev Test Wear": {
"prompt_template": (
"{subject_phrase}: clean test style. {item_label}: {item}. "
"Scene: {scene}. Pose: {pose}. Facial expression: {expression}. "
"Composition: {composition}."
),
"caption_template": "{subject_phrase}, {item}, {scene}, {composition}",
"subcategories": {
"Layered Office": {
"items": ["structured test blazer over dark trousers"],
"scenes": ["temporary showroom with modular shelves"],
"poses": ["standing by a rail"],
"expressions": ["focused calm look"],
"compositions": ["centered catalog frame"],
}
},
},
"Dev / Test Wear": {
"prompt_template": (
"{subject_phrase}: slash-name test style. {item_label}: {item}. "
"Scene: {scene}. Pose: {pose}. Facial expression: {expression}. "
"Composition: {composition}."
),
"caption_template": "{subject_phrase}, {item}, {scene}, {composition}",
"subcategories": {
"Layered / Office": {
"items": ["slash-safe structured blazer over tailored trousers"],
"scenes": ["slash-safe showroom with glass shelving"],
"poses": ["standing beside a mirrored divider"],
"expressions": ["focused exact-route look"],
"compositions": ["slash-safe centered catalog frame"],
}
},
},
}
},
ensure_ascii=True,
),
encoding="utf-8",
)
_expect("Dev Test Wear" in pb.category_choices(), "User-added JSON category did not reach category choices")
_expect(
"Dev Test Wear / Layered Office" in pb.subcategory_choices(),
"User-added JSON subcategory did not reach subcategory choices",
)
slash_selector = "Dev / Test Wear / Layered / Office"
_expect("Dev / Test Wear" in pb.category_choices(), "Slash-bearing user category did not reach category choices")
_expect(
slash_selector in pb.subcategory_choices(),
"Slash-bearing user subcategory did not reach subcategory choices",
)
row = pb.build_prompt(
category="Dev Test Wear",
subcategory="Dev Test Wear / Layered Office",
row_number=1,
start_index=1,
seed=4109,
clothing="random",
ethnicity="any",
poses="random",
backside_bias=0.0,
figure="random",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=0.0,
standard_pose_ratio=1.0,
trigger=Trigger,
prepend_trigger_to_prompt=True,
extra_positive="",
extra_negative="",
women_count=1,
men_count=0,
)
_expect_row_base(row, "category_extensions_user_added_json")
_expect(row.get("source") == "json_category", "User-added JSON category did not use custom row route")
_expect(row.get("main_category") == "Dev Test Wear", "User-added JSON category name drifted")
_expect(row.get("subcategory") == "Layered Office", "User-added JSON subcategory name drifted")
_expect(row.get("item") == "structured test blazer over dark trousers", "User-added JSON item did not generate")
_expect(row.get("scene_text") == "temporary showroom with modular shelves", "User-added JSON scene did not generate")
_expect(row.get("pose") == "standing by a rail", "User-added JSON pose did not generate")
_expect(row.get("expression") == "focused calm look", "User-added JSON expression did not generate")
_expect(row.get("composition") == "centered catalog frame", "User-added JSON composition did not generate")
_expect_formatter_outputs(row, "category_extensions_user_added_json", target="single")
slash_row = pb.build_prompt(
category="Dev / Test Wear",
subcategory=slash_selector,
row_number=1,
start_index=1,
seed=4110,
clothing="random",
ethnicity="any",
poses="random",
backside_bias=0.0,
figure="random",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=0.0,
standard_pose_ratio=1.0,
trigger=Trigger,
prepend_trigger_to_prompt=True,
extra_positive="",
extra_negative="",
women_count=1,
men_count=0,
)
_expect_row_base(slash_row, "category_extensions_slash_named_json")
_expect(slash_row.get("main_category") == "Dev / Test Wear", "Slash-bearing JSON category name drifted")
_expect(slash_row.get("subcategory") == "Layered / Office", "Slash-bearing JSON subcategory name drifted")
_expect(
slash_row.get("item") == "slash-safe structured blazer over tailored trousers",
"Slash-bearing JSON exact subcategory did not generate the intended item",
)
_expect_formatter_outputs(slash_row, "category_extensions_slash_named_json", target="single")
finally:
category_library.CATEGORY_DIR = previous_category_dir
category_extensions._EXTENSIONS_APPLIED = previous_extensions_applied
def smoke_category_cast_config_policy() -> None:
_expect(pb.CATEGORY_PRESETS is category_cast_config.CATEGORY_PRESETS, "Prompt builder category presets are not delegated")
_expect(pb.CAST_PRESETS is category_cast_config.CAST_PRESETS, "Prompt builder cast presets are not delegated")
_expect("woman" in category_cast_config.category_preset_choices(), "Category preset choices lost built-in woman")
_expect("man" in category_cast_config.category_preset_choices(), "Category preset choices lost built-in man")
_expect("hardcore_pose" in category_cast_config.category_preset_choices(), "Category preset choices lost hardcore_pose")
_expect("custom_counts" in category_cast_config.cast_preset_choices(), "Cast preset choices lost custom_counts")
_expect(
pb._count_phrase(2, "adult woman", "adult women") == cast_context.count_phrase(2, "adult woman", "adult women"),
"Prompt builder count phrase should delegate to cast_context",
)
configured = cast_context.configured_cast_context(1, 2)
_expect(configured.get("subject_phrase") == "one adult woman and two adult men", "Configured cast subject phrase changed")
_expect(configured.get("cast_summary") == "1 woman, 2 men, 3 total adults", "Configured cast summary changed")
_expect(configured.get("scene_kind") == "adult threesome sex scene", "Configured cast scene kind changed")
_expect(
pb._configured_cast_context(1, 2) == configured,
"Prompt builder configured cast context should delegate to cast_context",
)
_expect(
cast_context.couple_type_from_counts(
random.Random(1),
2,
0,
choose=lambda _rng, pool: pool[0],
couple_types=[("woman and man", "a woman and a man", "fallback pose")],
)
== ("two women", "two women", "close affectionate couple pose", 2, 0),
"Couple type count override for two women changed",
)
_expect(
pb._subject_context(random.Random(5), "couple", "any", "curvy", False, False, 1, 1)
== subject_context.subject_context(random.Random(5), "couple", "any", "curvy", False, False, 1, 1),
"Prompt builder subject context should delegate to subject_context",
)
_expect(
subject_context.subject_context(random.Random(1), "configured_cast", "any", "curvy", False, False, 2, 1).get("cast_summary")
== "2 women, 1 man, 3 total adults",
"Configured cast subject context changed",
)
group = subject_context.subject_context(random.Random(2), "group", "asian", "curvy", False, False)
_expect(group.get("subject") == "mixed Asian adult group", "Group subject ethnicity wording changed")
_expect(
subject_context.subject_context(random.Random(3), "layout", "any", "curvy", False, False).get("subject")
== "layout scene",
"Layout subject context fallback changed",
)
label_slots = [
{"subject_type": "woman", "label": "auto_chain", "name": "older auto"},
{"subject_type": "woman", "label": "auto_chain", "name": "newer auto"},
{"subject_type": "man", "label": "B", "name": "explicit man"},
]
label_map = cast_context.character_slot_label_map(label_slots)
_expect(
label_map.get("Woman A", {}).get("name") == "newer auto"
and label_map.get("Woman B", {}).get("name") == "older auto",
"Character slot auto-chain label order changed",
)
_expect(
label_map.get("Man B", {}).get("name") == "explicit man",
"Character slot explicit label mapping changed",
)
_expect(
pb._character_slot_label_map(label_slots) == label_map,
"Prompt builder character slot label map should delegate to cast_context",
)
category_config = json.loads(pb.build_category_config_json("hardcore_pose", "Foreplay and teasing"))
_expect(category_config.get("category") == "Hardcore sexual poses", "Category config lost hardcore category mapping")
_expect(category_config.get("subcategory") == "Foreplay and teasing", "Category config lost explicit subcategory")
_expect(pb._parse_category_config(category_config) == ("Hardcore sexual poses", "Foreplay and teasing"), "Category parser wrapper drifted")
builtin_woman_config = json.loads(pb.build_category_config_json("woman", "random"))
_expect(builtin_woman_config.get("preset") == "woman", "Built-in woman category preset fell back")
_expect(builtin_woman_config.get("category") == "woman", "Built-in woman category config lost direct category")
_expect(pb._parse_category_config(builtin_woman_config) == ("woman", "random"), "Built-in woman category parser drifted")
fallback_config = json.loads(category_cast_config.build_category_config_json("unknown", "random"))
_expect(fallback_config.get("preset") == "auto_weighted", "Unknown category preset did not fall back")
_expect(pb._parse_category_config({"preset": "unknown"}) == ("auto_weighted", "random"), "Unknown category parser fallback changed")
cast_config = json.loads(pb.build_cast_config_json("mixed_couple", 9, 9))
_expect((cast_config.get("women_count"), cast_config.get("men_count")) == (1, 1), "Cast preset did not override manual counts")
custom_cast = json.loads(category_cast_config.build_cast_config_json("custom_counts", -5, 99))
_expect((custom_cast.get("women_count"), custom_cast.get("men_count")) == (0, 12), "Custom cast counts were not clamped")
empty_cast = pb._parse_cast_config({"cast_mode": "custom_counts", "women_count": 0, "men_count": 0})
_expect((empty_cast.get("women_count"), empty_cast.get("men_count")) == (1, 0), "Empty custom cast was not corrected")
def smoke_row_subject_route_policy() -> None:
seed_cfg = seed_config.parse_seed_config({})
slot_cast = pb.build_character_slot_json(
subject_type="woman",
label="A",
age="32-year-old adult",
ethnicity="western_european",
figure="balanced",
body="slim",
hair="short silver bob",
eyes="gray eyes",
descriptor_detail="full",
)["character_cast"]
profile = {
"profile_type": "character",
"subject_type": "woman",
"age": "45-year-old adult",
"body": "average",
"body_phrase": "average figure",
"skin": "profile skin",
"hair": "profile hair",
"eyes": "profile eyes",
}
route = row_subject_route.resolve_subject_route(
subject_type="woman",
seed_config=seed_cfg,
seed=501,
row_number=1,
ethnicity="any",
figure="balanced",
no_plus_women=False,
no_black=False,
women_count=1,
men_count=0,
character_profile=profile,
character_cast=slot_cast,
)
delegated = pb._subject_route(
subject_type="woman",
seed_config=seed_cfg,
seed=501,
row_number=1,
ethnicity="any",
figure="balanced",
no_plus_women=False,
no_black=False,
women_count=1,
men_count=0,
character_profile=profile,
character_cast=slot_cast,
)
_expect(delegated == route, "Prompt builder subject route should delegate to row_subject_route")
_expect(route["subject_type"] == "woman", "Subject route changed single-woman subject type")
_expect(route["character_slot_status"] == "applied:Woman A", "Subject route did not apply matching Woman A slot")
_expect(route["character_profile_status"] == "skipped_character_slot", "Subject route should skip profile when slot applies")
_expect(route["context"].get("age") == "32-year-old adult", "Subject route lost slot age override")
_expect(route["context"].get("hair") == "short silver bob", "Subject route lost slot hair override")
_expect(route["applied_profile"] == {}, "Subject route should not apply profile over matching slot")
cast_route = row_subject_route.resolve_subject_route(
subject_type="configured_cast",
seed_config=seed_cfg,
seed=502,
row_number=1,
ethnicity="western_european",
figure="balanced",
no_plus_women=False,
no_black=False,
women_count=1,
men_count=1,
character_cast=_character_cast(pov_man=True),
)
_expect(cast_route["subject_type"] == "configured_cast", "Configured-cast subject route changed subject type")
_expect(cast_route["pov_character_labels"] == ["Man A"], "Subject route lost configured-cast POV man label")
_expect("Woman A:" in cast_route["cast_descriptor_text"], "Subject route lost visible woman descriptor")
_expect("Man A:" not in cast_route["cast_descriptor_text"], "Subject route should not describe POV man as visible cast")
def smoke_generation_profile_config_policy() -> None:
_expect(
pb.GENERATION_PROFILE_PRESETS is generation_profile_config.GENERATION_PROFILE_PRESETS,
"Prompt builder generation profile presets are not delegated",
)
_expect("krea2_friendly" in generation_profile_config.generation_profile_choices(), "Generation profile choices lost krea2_friendly")
profile = json.loads(
pb.build_generation_profile_json(
profile="krea2_friendly",
clothing_override="minimal",
poses_override="random",
expression_enabled=False,
expression_intensity_mode="random",
expression_intensity=0.8,
backside_bias=2,
minimal_clothing_ratio=0.25,
standard_pose_ratio=0.75,
trigger_policy="prepend_trigger",
)
)
_expect(profile.get("profile") == "krea2_friendly", "Generation profile output lost selected profile")
_expect(profile.get("clothing") == "minimal", "Generation profile clothing override failed")
_expect(profile.get("poses") == "random", "Generation profile poses override failed")
_expect(profile.get("expression_enabled") is False, "Generation profile expression disable failed")
_expect(profile.get("expression_intensity") == -1.0, "Generation profile random expression marker changed")
_expect(profile.get("backside_bias") == 1.0, "Generation profile backside bias clamp changed")
_expect(profile.get("prepend_trigger_to_prompt") is True, "Generation profile trigger override failed")
parsed = pb._parse_generation_profile(profile)
_expect(parsed.get("clothing") == "minimal", "Generation profile parser wrapper lost clothing")
_expect(parsed.get("expression_enabled") is False, "Generation profile parser wrapper lost expression disable")
_expect(parsed.get("minimal_clothing_ratio") == 0.25, "Generation profile parser wrapper lost minimal clothing ratio")
fallback = generation_profile_config.parse_generation_profile({"profile": "unknown", "clothing": "bad", "poses": "bad"})
_expect(fallback.get("profile") == "unknown", "Generation profile parser should preserve raw profile label")
_expect(fallback.get("clothing") == "full", "Generation profile parser did not normalize invalid clothing")
_expect(fallback.get("poses") == "standard", "Generation profile parser did not normalize invalid poses")
_expect(fallback.get("trigger") == "sxcpinup_coloredpencil", "Generation profile parser lost default trigger")
def smoke_filter_config_policy() -> None:
_expect(pb.ETHNICITY_FILTER_CHOICES is filter_config.ETHNICITY_FILTER_CHOICES, "Prompt builder ethnicity choices are not delegated")
_expect("french_european" in filter_config.ETHNICITY_LIST_KEYS, "Ethnicity list keys lost regional choices")
advanced = json.loads(
pb.build_filter_config_json(
include_european=True,
include_mediterranean_mena=False,
include_latina=False,
include_east_asian=False,
include_southeast_asian=False,
include_south_asian=False,
include_black_african=True,
include_indigenous=False,
include_mixed=False,
include_plus_size=False,
figure="bad",
)
)
_expect(advanced.get("ethnicity_includes") == ["european", "black_african"], "Advanced filter selected ethnicity list changed")
_expect("exclude_latina" in advanced.get("ethnicity", ""), "Advanced filter ethnicity excludes changed")
_expect(advanced.get("figure") == "curvy", "Advanced filter invalid figure fallback changed")
_expect(advanced.get("no_plus_women") is True, "Advanced filter plus-size exclusion changed")
ethnicity_list = pb.build_ethnicity_list_json(include_french_european=True, include_asian=True, strict_excludes=True)
_expect("french_european" in ethnicity_list["ethnicity"], "Ethnicity list lost regional include")
_expect("asian" in ethnicity_list["ethnicity"], "Ethnicity list lost umbrella Asian include")
_expect("exclude_european" not in ethnicity_list["ethnicity"], "Ethnicity list should protect European when regional Europe is selected")
_expect("exclude_east_asian" not in ethnicity_list["ethnicity"], "Ethnicity list should protect East Asian when Asian is selected")
_expect("filter_config" in ethnicity_list, "Ethnicity list lost filter_config output")
parsed_text = pb._parse_filter_config("french_european")
_expect(parsed_text.get("ethnicity") == "french_european", "Filter parser text shortcut changed")
parsed_bad = filter_config.parse_filter_config({"ethnicity": "bad", "figure": "bad"})
_expect(parsed_bad.get("ethnicity") == "any", "Filter parser invalid ethnicity fallback changed")
_expect(parsed_bad.get("figure") == "curvy", "Filter parser invalid figure fallback changed")
_expect(pb.normalize_ethnicity_filter("random", "any", allow_random=True) == "random", "Ethnicity random normalization changed")
_expect(pb.normalize_ethnicity_filter("random", "any", allow_random=False) == "any", "Ethnicity default normalization changed")
def smoke_character_config_policy() -> None:
_expect(pb.CHARACTER_LABEL_CHOICES is character_config.CHARACTER_LABEL_CHOICES, "Prompt builder character choices are not delegated")
_expect("21-year-old adult" in character_config.character_age_choices(), "Character age choices lost adult ages")
_expect("fat" in character_config.character_man_body_choices(), "Man body pool lost fat option")
_expect(pb.character_figure_choices() == character_config.character_figure_choices(), "Character figure choices should delegate")
_expect("platinum_blonde" in character_config.character_hair_color_choices(), "Hair color choices lost platinum blonde")
traits = json.loads(
pb.build_characteristics_config_json(
axis="bodies",
selected_values=["slim", "bad value", "slim", "fat"],
combine_mode="replace_axis",
)
)
_expect(traits.get("bodies") == ["slim", "fat"], "Character body trait normalization changed")
merged_traits = json.loads(
character_config.build_characteristics_config_json(
characteristics=traits,
axis="eyes",
selected_values=["blue", "gray-brown", "blue"],
combine_mode="add_to_axis",
)
)
_expect(merged_traits.get("bodies") == ["slim", "fat"], "Character trait merge lost existing axis")
_expect(merged_traits.get("eyes") == ["blue", "gray_brown"], "Character eye trait normalization changed")
_expect(pb._characteristic_choice({"ages": ["21-year-old adult"]}, "ages", random.Random(1)) == "21-year-old adult", "Trait choice changed")
hair = json.loads(
pb.build_hair_config_json(
axis="color",
selected_values=["platinum blonde", "bad", "dark-brown"],
combine_mode="replace_axis",
)
)
_expect(hair.get("colors") == ["platinum_blonde", "dark_brown"], "Hair color normalization changed")
hair = json.loads(
character_config.build_hair_config_json(
hair_config=hair,
axis="style",
selected_values=["messy bun", "straight"],
combine_mode="add_to_axis",
)
)
_expect(hair.get("styles") == ["messy_bun", "straight"], "Hair style config merge changed")
_expect(pb._hair_phrase_from_parts("platinum_blonde", "long", "messy_bun") == "long platinum-blonde hair in a messy bun", "Hair phrase helper changed")
_expect(character_config.normalize_presence_mode("pov", "woman") == "visible", "POV presence should stay man-only")
pov_slot = {"subject_type": "man", "presence_mode": "pov"}
visible_slot = {"subject_type": "man", "presence_mode": "visible"}
_expect(pb._slot_is_pov(pov_slot) is True, "Prompt builder POV slot helper should delegate to POV policy")
_expect(pov_policy.slot_is_pov(visible_slot) is False, "Visible man slot should not be POV")
_expect(
pb._pov_character_labels({"Man A": pov_slot, "Man B": visible_slot}, 2) == ["Man A"],
"POV label selection should keep only POV men in count order",
)
_expect(
pb._pov_role_graph_prompt("Man A is positioned behind Woman A", ["Man A"])
== "First-person POV from Man A; the POV camera is positioned behind Woman A",
"Builder POV role graph prompt should use shared viewer replacement",
)
_expect(
pb._pov_composition_prompt("wide group-sex composition with all bodies visible", ["Man A"])
== "first-person group-sex POV composition with visible partners readable",
"Builder POV composition prompt should use shared POV composition replacements",
)
_expect(
krea_pov.pov_composition_text(
"wide group-sex composition with all bodies visible, adapted for first-person POV with the POV participant kept off-camera",
["Man A"],
)
== "first-person group-sex POV composition with visible partners readable",
"Krea POV composition cleanup should delegate shared replacements and strip builder annotation",
)
_expect(character_config.normalize_slot_seed(0xFFFFFFFF + 99) == 0xFFFFFFFF, "Slot seed clamp changed")
slot_result = character_slot.build_character_slot_json(
subject_type="man",
label="Man B",
slot_seed=123,
age="manual",
manual_age="44-year-old adult",
ethnicity="western_european",
figure="balanced",
body="manual",
manual_body="stocky",
descriptor_detail="compact",
expression_intensity=1.5,
softcore_expression_intensity=0.25,
hardcore_expression_intensity=-1,
presence_mode="pov",
hair_color="dark brown",
hair_length="short",
hair_style="straight",
softcore_outfit="buttoned shirt",
hardcore_clothing="shirt pushed open",
)
_expect(
pb.build_character_slot_json(
subject_type="man",
label="Man B",
slot_seed=123,
age="manual",
manual_age="44-year-old adult",
ethnicity="western_european",
figure="balanced",
body="manual",
manual_body="stocky",
descriptor_detail="compact",
expression_intensity=1.5,
softcore_expression_intensity=0.25,
hardcore_expression_intensity=-1,
presence_mode="pov",
hair_color="dark brown",
hair_length="short",
hair_style="straight",
softcore_outfit="buttoned shirt",
hardcore_clothing="shirt pushed open",
)
== slot_result,
"Prompt builder character slot JSON should delegate to character_slot",
)
slot = json.loads(slot_result["character_slot"])
_expect(slot.get("age") == "44-year-old adult", "Character slot manual age normalization changed")
_expect(slot.get("body") == "stocky", "Character slot manual body normalization changed")
_expect(slot.get("presence_mode") == "pov", "Character slot POV presence normalization changed")
_expect(slot.get("expression_intensity") == 1.0, "Character slot expression intensity clamp changed")
_expect(
character_slot.slot_expression_intensity_for_phase(slot, "softcore") == 0.25
and character_slot.slot_expression_intensity_for_phase(slot, "hardcore") == 1.0,
"Character slot phase expression fallback changed",
)
_expect(
pb._slot_effective_figure({"slot_seed": 123, "figure": "random"}, "woman", "curvy")
== character_slot.slot_effective_figure({"slot_seed": 123, "figure": "random"}, "woman", "curvy"),
"Prompt builder seeded slot figure should delegate to character_slot",
)
_expect(
pb._appearance_for_subject(random.Random(9), "woman", "western_european", "balanced", False, False)
== character_appearance.appearance_for_subject(random.Random(9), "woman", "western_european", "balanced", False, False),
"Prompt builder appearance selection should delegate to character_appearance",
)
_expect(
pb._context_from_character_slot(random.Random(11), slot, "man", "any", "curvy", False, False)
== character_appearance.context_from_character_slot(random.Random(11), slot, "man", "any", "curvy", False, False),
"Prompt builder slot context should delegate to character_appearance",
)
_expect(
pb._row_from_character_slot(slot_result["character_slot"])
== character_appearance.row_from_character_slot(slot_result["character_slot"]),
"Prompt builder slot row conversion should delegate to character_appearance",
)
row = character_appearance.apply_character_context_to_row({}, {"age": "44-year-old adult", "body": "stocky"})
_expect(row.get("age_band") == "44-year-old adult" and row.get("body") == "stocky", "Character context row application changed")
def smoke_character_profile_policy() -> None:
_expect(pb.CHARACTER_MANUAL_FIELDS is character_profile.CHARACTER_MANUAL_FIELDS, "Prompt builder manual fields are not delegated")
_expect(pb.PROFILE_DIR == character_profile.PROFILE_DIR, "Prompt builder profile dir is not delegated")
_expect(pb._body_phrase("curvy", "hourglass figure") == "curvy build and hourglass figure", "Body phrase helper changed")
_expect(pb._safe_profile_name("bad name!*") == "bad_name", "Profile name sanitizer changed")
manual = json.loads(
pb.build_character_manual_config_json(
combine_mode="merge_nonempty",
manual_age="31-year-old adult",
body_phrase="custom body",
skin="warm skin",
softcore_outfit="red dress",
)
)
_expect(manual.get("manual_age") == "31-year-old adult", "Manual config lost age")
_expect(manual.get("softcore_outfit") == "red dress", "Manual config lost outfit")
_expect("manual_age=31-year-old adult" in manual.get("summary", ""), "Manual config summary changed")
metadata_row = {
"subject_type": "woman",
"age": "28-year-old adult",
"body": "curvy",
"body_phrase": "curvy figure with full hips",
"skin": "warm skin",
"hair": "long black hair",
"eyes": "brown eyes",
"figure": "balanced",
"descriptor_detail": "medium",
}
profile_result = character_profile.build_character_profile_json(
profile_name="smoke profile",
source="metadata_json",
metadata_json=metadata_row,
)
profile = json.loads(profile_result["profile_json"])
_expect(profile.get("profile_name") == "smoke_profile", "Profile name normalization changed")
_expect(profile.get("age") == "28-year-old adult", "Profile metadata extraction lost age")
_expect("long black hair" in profile_result["descriptor"], "Profile descriptor lost hair at medium detail")
loaded = pb.load_character_profile_json(
profile_name="manual",
fallback_profile_json=profile_result["profile_json"],
override_age="35-year-old adult",
override_descriptor_detail="compact",
)
loaded_profile = json.loads(loaded["profile_json"])
_expect(loaded.get("status") == "fallback", "Profile fallback load status changed")
_expect(loaded_profile.get("age") == "35-year-old adult", "Profile override age did not apply")
_expect(loaded_profile.get("descriptor_detail") == "compact", "Profile override descriptor detail did not apply")
context = {"subject_type": "woman", "subject": "woman", "subject_phrase": "woman", "age": "21-year-old adult"}
applied_context, applied_profile, status = pb._apply_character_profile_to_context(context, loaded_profile)
_expect(status == "applied", "Profile context application changed")
_expect(applied_context.get("age") == "35-year-old adult", "Profile context application lost age")
_expect(applied_profile.get("profile_type") == "character", "Profile context returned wrong profile")
def smoke_row_normalization_policy() -> None:
_expect(
pb._prepend_trigger("base prompt", Trigger, True) == row_normalization.prepend_trigger("base prompt", Trigger, True),
"Prompt builder trigger helper should delegate to row normalization policy",
)
_expect(
pb._combined_negative("bad anatomy", "low quality") == row_normalization.combined_negative("bad anatomy", "low quality"),
"Prompt builder negative helper should delegate to row normalization policy",
)
row = row_normalization.normalize_prompt_row(
{
"prompt": f"{Trigger}, {Trigger}, base prompt.",
"caption": f"{Trigger}, {Trigger}, base caption.",
"negative_prompt": "bad anatomy, bad anatomy",
},
active_trigger=Trigger,
prepend_trigger_to_prompt=True,
extra_positive="extra detail",
extra_negative="low quality, bad anatomy",
default_negative="bad anatomy",
)
_expect_trigger_once("row_normalization.prompt", row.get("prompt"), Trigger)
_expect_trigger_once("row_normalization.caption", row.get("caption"), Trigger)
_expect("extra detail" in row.get("prompt", ""), "Row normalization lost extra positive text")
_expect(row.get("trigger") == Trigger, "Row normalization lost active trigger")
_expect_no_duplicate_comma_items("row_normalization.negative", row.get("negative_prompt"))
legacy_couple = row_normalization.normalize_prompt_row(
{
"source": "built_in_generator",
"primary_subject": "two women",
"scene": "office",
"prompt": (
"Two adults in a clean legacy prompt. Scene: old room. "
"Pose: standing close, affectionate and flirtatious but non-explicit. "
"Facial expressions: one with a calm smile, the other with a side glance. "
"Clothing: coordinated satin outfits, resort styling. Composition: vertical old frame."
),
"caption": "legacy couple caption",
"negative_prompt": "bad anatomy",
},
active_trigger=Trigger,
prepend_trigger_to_prompt=False,
)
_expect(legacy_couple.get("subject_type") == "couple", "Legacy couple row lost normalized subject_type")
_expect(legacy_couple.get("women_count") == 2 and legacy_couple.get("men_count") == 0, "Legacy couple row lost normalized counts")
_expect(legacy_couple.get("person_count") == 2, "Legacy couple row lost normalized person count")
_expect(legacy_couple.get("scene_slug") == "office", "Legacy couple row lost scene slug")
_expect("cozy office desk" in str(legacy_couple.get("scene_text", "")), "Legacy couple row lost readable scene text")
_expect(legacy_couple.get("scene_entry", {}).get("slug") == "office", "Legacy couple row lost scene entry")
_expect(legacy_couple.get("pose") == "standing close", "Legacy couple row did not clean pose suffix")
_expect(legacy_couple.get("item") == "coordinated satin outfits", "Legacy couple row did not clean clothing suffix")
_expect(legacy_couple.get("item_label") == "Clothing", "Legacy couple row lost item label")
_expect("calm smile" in str(legacy_couple.get("expression", "")), "Legacy couple row lost expression metadata")
_expect("cast_summary" not in legacy_couple, "Legacy couple row should not gain configured-cast summary")
legacy_single = row_normalization.normalize_prompt_row(
{
"source": "built_in_generator",
"primary_subject": "woman",
"scene": "studio",
"age_band": "25-year-old adult",
"body_type": "curvy",
"figure": "soft curves, defined waist",
"caption": (
f"{Trigger}, woman, 25-year-old adult, curvy figure with soft curves, defined waist, "
"warm skin, short blonde hair, blue eyes, pose, expression, clothing, scene, composition"
),
"prompt": (
"A woman. Scene: old studio. Pose: standing calmly. "
"Facial expression: direct look. Clothing: fitted dress, fashion editorial styling. "
"Composition: vertical portrait."
),
"negative_prompt": "bad anatomy",
},
active_trigger=Trigger,
prepend_trigger_to_prompt=False,
)
_expect(legacy_single.get("body_phrase") == "curvy figure with soft curves, defined waist", "Legacy single row lost body phrase")
_expect(legacy_single.get("skin") == "warm skin", "Legacy single row lost skin metadata")
_expect(legacy_single.get("hair") == "short blonde hair", "Legacy single row lost hair metadata")
_expect(legacy_single.get("eyes") == "blue eyes", "Legacy single row lost eye metadata")
legacy_group = row_normalization.normalize_prompt_row(
{
"source": "built_in_generator",
"primary_subject": "mixed adult group",
"prompt": "Group legacy prompt.",
"caption": "legacy group caption",
"negative_prompt": "bad anatomy",
},
active_trigger=Trigger,
prepend_trigger_to_prompt=False,
)
_expect(legacy_group.get("subject_type") == "group", "Legacy group row lost normalized subject_type")
_expect(legacy_group.get("women_count") == 2 and legacy_group.get("men_count") == 2, "Legacy group row lost fallback counts")
_expect(legacy_group.get("person_count") == 4, "Legacy group row lost normalized person count")
legacy_layout = row_normalization.normalize_prompt_row(
{
"source": "built_in_generator",
"primary_subject": "layout scene",
"prompt": "Layout legacy prompt.",
"caption": "legacy layout caption",
"negative_prompt": "bad anatomy",
},
active_trigger=Trigger,
prepend_trigger_to_prompt=False,
)
_expect(legacy_layout.get("subject_type") == "layout", "Legacy layout row lost normalized subject_type")
_expect("women_count" not in legacy_layout and "men_count" not in legacy_layout, "Legacy layout row should not invent cast counts")
outputs = row_normalization.normalize_pair_text_outputs(
active_trigger=Trigger,
prepend_trigger_to_prompt=True,
extra_positive="pair extra",
extra_negative="low quality, bad anatomy",
soft_prompt="soft prompt.",
hard_prompt="hard prompt.",
soft_negative_base="bad anatomy, bad anatomy",
hard_negative_base="bad anatomy, low quality",
soft_caption_parts=[Trigger, "soft caption"],
hard_caption_parts=[Trigger, "hard caption"],
)
_expect_trigger_once("row_normalization.soft_prompt", outputs.get("soft_prompt"), Trigger)
_expect_trigger_once("row_normalization.hard_prompt", outputs.get("hard_prompt"), Trigger)
_expect_trigger_once("row_normalization.soft_caption", outputs.get("soft_caption"), Trigger)
_expect_trigger_once("row_normalization.hard_caption", outputs.get("hard_caption"), Trigger)
_expect_no_duplicate_comma_items("row_normalization.soft_negative", outputs.get("soft_negative"))
_expect_no_duplicate_comma_items("row_normalization.hard_negative", outputs.get("hard_negative"))
pair = row_normalization.normalize_pair_metadata(
{
"softcore_prompt": f"{Trigger}, {Trigger}, soft pair.",
"hardcore_prompt": f"{Trigger}, {Trigger}, hard pair.",
"softcore_caption": f"{Trigger}, {Trigger}, soft caption.",
"hardcore_caption": f"{Trigger}, {Trigger}, hard caption.",
"softcore_negative_prompt": "bad anatomy, bad anatomy",
"hardcore_negative_prompt": "bad anatomy, low quality, bad anatomy",
"softcore_partner_styling": {"outfits": ["partner outfit"], "pose": "partner pose"},
"hardcore_clothing_state": "structured hard clothing state",
"character_hardcore_clothing": ["Woman A custom hard clothing"],
"default_man_hardcore_clothing": ["Man A default hard clothing"],
"hardcore_detail_density": "dense",
"hardcore_position_config": {"family": "oral"},
"softcore_camera_config": {"camera_mode": "standard", "camera_detail": "compact"},
"hardcore_camera_config": {"camera_mode": "pov", "camera_detail": "dense"},
"softcore_camera_directive": "Camera: soft front view.",
"hardcore_camera_directive": "Camera: hard side view.",
"softcore_camera_scene_directive": "Soft camera-aware scene layout.",
"hardcore_camera_scene_directive": "Hard camera-aware scene layout.",
"softcore_row": {
"prompt": f"{Trigger}, {Trigger}, embedded soft.",
"caption": f"{Trigger}, {Trigger}, embedded soft caption.",
"negative_prompt": "bad anatomy, bad anatomy",
"camera_directive": "stale soft camera",
},
"hardcore_row": {
"prompt": f"{Trigger}, {Trigger}, embedded hard.",
"caption": f"{Trigger}, {Trigger}, embedded hard caption.",
"negative_prompt": "low quality, bad anatomy, low quality",
"camera_scene_directive": "stale hard camera scene",
},
},
active_trigger=Trigger,
)
_expect_trigger_once("row_normalization.pair.softcore_prompt", pair.get("softcore_prompt"), Trigger)
_expect_trigger_once("row_normalization.pair.hardcore_prompt", pair.get("hardcore_prompt"), Trigger)
_expect_trigger_once("row_normalization.pair.softcore_row.prompt", pair["softcore_row"].get("prompt"), Trigger)
_expect_trigger_once("row_normalization.pair.hardcore_row.caption", pair["hardcore_row"].get("caption"), Trigger)
_expect(
pair["softcore_row"].get("prompt") == pair.get("softcore_prompt"),
"Pair normalization left stale soft row prompt text",
)
_expect(
pair["hardcore_row"].get("caption") == pair.get("hardcore_caption"),
"Pair normalization left stale hard row caption text",
)
_expect(
pair["softcore_row"].get("softcore_partner_styling") == pair.get("softcore_partner_styling"),
"Pair normalization left stale soft side metadata",
)
_expect(
pair["hardcore_row"].get("hardcore_clothing_state") == pair.get("hardcore_clothing_state"),
"Pair normalization left stale hard clothing metadata",
)
_expect(
pair["hardcore_row"].get("default_man_hardcore_clothing") == pair.get("default_man_hardcore_clothing"),
"Pair normalization left stale hard default clothing metadata",
)
_expect(
pair["softcore_row"].get("camera_config") == pair.get("softcore_camera_config"),
"Pair normalization left stale soft camera config",
)
_expect(
pair["hardcore_row"].get("camera_config") == pair.get("hardcore_camera_config"),
"Pair normalization left stale hard camera config",
)
_expect(
pair["softcore_row"].get("camera_directive") == pair.get("softcore_camera_directive"),
"Pair normalization left stale soft camera directive",
)
_expect(
pair["hardcore_row"].get("camera_scene_directive") == pair.get("hardcore_camera_scene_directive"),
"Pair normalization left stale hard camera scene directive",
)
_expect_no_duplicate_comma_items("row_normalization.pair.soft_negative", pair.get("softcore_negative_prompt"))
_expect_no_duplicate_comma_items("row_normalization.pair.hard_row_negative", pair["hardcore_row"].get("negative_prompt"))
reverse_pair = row_normalization.normalize_pair_metadata(
{
"softcore_row": {
"prompt": f"{Trigger}, {Trigger}, embedded-only soft.",
"caption": f"{Trigger}, {Trigger}, embedded-only soft caption.",
"negative_prompt": "bad anatomy, bad anatomy",
"softcore_partner_styling": {"outfits": ["row partner outfit"], "pose": "row partner pose"},
"camera_config": {"camera_mode": "standard"},
"camera_directive": "Camera: row soft front view.",
"camera_scene_directive": "Row soft scene camera layout.",
},
"hardcore_row": {
"prompt": f"{Trigger}, {Trigger}, embedded-only hard.",
"caption": f"{Trigger}, {Trigger}, embedded-only hard caption.",
"negative_prompt": "low quality, low quality",
"hardcore_clothing_state": "row hard clothing state",
"character_hardcore_clothing": ["Woman A row hard clothing"],
"default_man_hardcore_clothing": ["Man A row default hard clothing"],
"hardcore_detail_density": "concise",
"hardcore_position_config": {"family": "outercourse"},
"camera_config": {"camera_mode": "pov"},
"camera_directive": "Camera: row hard side view.",
"camera_scene_directive": "Row hard scene camera layout.",
},
},
active_trigger=Trigger,
)
_expect_trigger_once("row_normalization.reverse.soft_prompt", reverse_pair.get("softcore_prompt"), Trigger)
_expect_trigger_once("row_normalization.reverse.hard_caption", reverse_pair.get("hardcore_caption"), Trigger)
_expect(
reverse_pair.get("softcore_partner_styling") == reverse_pair["softcore_row"].get("softcore_partner_styling"),
"Pair normalization did not lift soft side metadata from embedded row",
)
_expect(
reverse_pair.get("hardcore_clothing_state") == reverse_pair["hardcore_row"].get("hardcore_clothing_state"),
"Pair normalization did not lift hard side metadata from embedded row",
)
_expect(
reverse_pair.get("softcore_camera_config") == reverse_pair["softcore_row"].get("camera_config"),
"Pair normalization did not lift soft camera config from embedded row",
)
_expect(
reverse_pair.get("hardcore_camera_scene_directive") == reverse_pair["hardcore_row"].get("camera_scene_directive"),
"Pair normalization did not lift hard camera scene from embedded row",
)
_expect_no_duplicate_comma_items("row_normalization.reverse.hard_negative", reverse_pair.get("hardcore_negative_prompt"))
def smoke_prompt_hygiene_policy() -> None:
merged = prompt_hygiene.combine_negative_text(
"bad anatomy, bad anatomy",
"low quality",
"bad anatomy",
"",
)
_expect(merged == "bad anatomy, low quality", "Prompt hygiene negative merge/dedupe changed")
_expect(
row_normalization.combined_negative("bad anatomy, bad anatomy", "low quality, bad anatomy") == merged,
"Row normalization negative merge should delegate to prompt hygiene",
)
_expect(
krea_formatter._combine_negative("bad anatomy, bad anatomy", "low quality", "bad anatomy") == merged,
"Krea negative merge should delegate to prompt hygiene",
)
_expect(
prompt_hygiene.sanitize_prose_text("Scene: . A sentence. A sentence.") == "A sentence.",
"Prompt hygiene prose cleanup changed",
)
def smoke_row_rendering_policy() -> None:
_expect(pb.SINGLE_TEMPLATE == row_rendering.SINGLE_TEMPLATE, "Prompt builder single template should delegate to row_rendering")
_expect(
pb._format("Known {known}, missing {missing}", {"known": 7})
== row_rendering.format_template("Known {known}, missing {missing}", {"known": 7}),
"Prompt builder safe formatter should delegate to row_rendering",
)
_expect(
row_rendering.format_template("Known {known}, missing {missing}", {"known": 7}) == "Known 7, missing {missing}",
"Row rendering changed missing-field preservation",
)
_expect(
row_rendering.prompt_template_for({}, {}, {}, "woman") == row_rendering.SINGLE_TEMPLATE,
"Row rendering default woman template changed",
)
_expect(
row_rendering.prompt_template_for({}, {}, {}, "group") == row_rendering.GROUP_TEMPLATE,
"Row rendering default group template changed",
)
category_text = {
"name": "Category Label",
"negative_prompt": "category negative",
"positive_suffix": "category suffix",
"style": "category style",
"item_label": "Category Item",
}
subcategory_text = {
"negative_prompt": "subcategory negative",
"positive_suffix": "subcategory suffix",
"style": "subcategory style",
}
item_text = {
"negative_prompt": "item negative",
"style": "item style",
}
_expect(
pb._row_text_fields(category_text, subcategory_text, item_text)
== row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text),
"Prompt builder row text field wrapper should delegate to row_rendering",
)
text_fields = row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text)
_expect(text_fields.negative_prompt == "item negative", "Row text fields did not prefer item negative prompt")
_expect(text_fields.positive_suffix == "subcategory suffix", "Row text fields did not prefer subcategory suffix")
_expect(text_fields.style == "item style", "Row text fields did not prefer item style")
_expect(text_fields.item_label == "Category Item", "Row text fields did not fall back to category item label")
default_text_fields = row_rendering.resolve_row_text_fields({"name": "Default Category"}, {}, {})
_expect(default_text_fields.negative_prompt, "Row text fields lost default negative prompt")
_expect(default_text_fields.positive_suffix == row_rendering.GENERIC_POSITIVE_SUFFIX, "Row text fields lost suffix default")
_expect(default_text_fields.style == row_rendering.DEFAULT_STYLE, "Row text fields lost style default")
_expect(default_text_fields.item_label == "Default Category", "Row text fields lost category-name label default")
style_config = pb.build_style_config_json(preset="comic_pinup_colored_pencil")
styled_fields = row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text, style_config)
_expect("comic pin-up" in styled_fields.style, "Style Pool did not override row style")
_expect("comic linework" in styled_fields.positive_suffix, "Style Pool did not override row style suffix")
negative_style_config = pb.build_style_config_json(
preset="realistic_photo",
custom_negative="flat vector, comic paper texture",
)
negative_fields = row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text, negative_style_config)
_expect("comic paper texture" in negative_fields.negative_prompt, "Style Pool did not merge style negatives")
context = {
"trigger": Trigger,
"subject": "configured cast",
"subject_phrase": "configured adult cast",
"age": "adult",
"body": "varied",
"body_phrase": "varied",
"skin": "",
"hair": "",
"eyes": "",
"item_label": "Scene",
"item": "shared action",
"scene": "warm room",
"pose": "standing close",
"expression": "focused look",
"composition": "centered frame",
"composition_prompt": "vertical centered frame",
"positive_suffix": "clear readable bodies.",
"negative_prompt": "bad anatomy",
"cast_descriptors": "Woman A: adult woman; Man A: adult man",
}
rendered = row_rendering.render_prompt_caption(
item={},
subcategory={
"prompt_template": "Scene: {item}. Composition: {composition_prompt}. Avoid: {negative_prompt}.",
"caption_template": "{trigger}, {item}, {scene}",
},
category={},
subject_type="configured_cast",
context=context,
cast_descriptor_text="Woman A: adult woman; Man A: adult man",
pov_prompt_directive="First-person POV from Man A.",
)
prompt = rendered["prompt"]
caption = rendered["caption"]
_expect("Characters: Woman A: adult woman; Man A: adult man." in prompt, "Row rendering lost configured-cast descriptors")
_expect("First-person POV from Man A." in prompt, "Row rendering lost configured-cast POV directive")
_expect(
prompt.index("Characters:") < prompt.index("First-person POV") < prompt.index("Avoid:"),
"Row rendering did not insert configured-cast directives before negative prompt",
)
_expect(
caption.endswith("Woman A: adult woman; Man A: adult man"),
"Row rendering did not append descriptors to captions without descriptor placeholders",
)
def smoke_row_role_graph_policy() -> None:
empty_route = row_role_graph.resolve_role_graph_route(
rng=random.Random(51),
subcategory={"slug": "penetration"},
context={"subject_type": "woman"},
item_axis_values={"position": "missionary"},
pov_character_labels=[],
is_pose_category=True,
)
_expect(empty_route == row_role_graph.RoleGraphRoute("", ""), "Role graph route should stay empty outside configured cast")
context = {
"subject_type": "configured_cast",
"women_count": "1",
"men_count": "1",
}
subcategory = {"slug": "cumshot_climax", "name": "Cumshot and climax"}
axis_values = {"position": "lying at the bed edge with thighs open"}
route = row_role_graph.resolve_role_graph_route(
rng=random.Random(52),
subcategory=subcategory,
context=context,
item_axis_values=axis_values,
pov_character_labels=[],
is_pose_category=True,
)
delegated = pb._role_graph_route(
rng=random.Random(52),
subcategory=subcategory,
context=context,
item_axis_values=axis_values,
pov_character_labels=[],
is_pose_category=True,
)
_expect(route == delegated, "Prompt builder role graph route wrapper should delegate to row_role_graph")
_expect("raised edge" in route.source_role_graph, "Role graph route did not sanitize bed-edge environment anchor")
_expect("bed edge" not in route.source_role_graph.lower(), "Role graph route leaked bed-edge environment anchor")
_expect(route.role_graph == route.source_role_graph, "Role graph route changed non-POV role graph text")
pov_route = row_role_graph.resolve_role_graph_route(
rng=random.Random(53),
subcategory={"slug": "oral", "name": "Oral"},
context=context,
item_axis_values={"position": "standing oral", "act": "blowjob"},
pov_character_labels=["Man A"],
is_pose_category=False,
)
_expect(pov_route.source_role_graph, "Role graph route lost POV source role graph")
_expect(
pov_route.role_graph.startswith("First-person POV from Man A;"),
"Role graph route did not prepend POV role graph directive",
)
structured_axis_route = row_role_graph.resolve_role_graph_route(
rng=random.Random(54),
subcategory={"slug": "oral", "name": "Oral"},
context=context,
item_axis_values={
"position": {"text": "standing oral position"},
"oral_act": ["blowjob", "auto"],
"contact_detail": {"unused": "mouth contact at hip height"},
},
pov_character_labels=[],
is_pose_category=False,
)
_expect(
"kneels in front" in structured_axis_route.source_role_graph,
"Role graph route should read structured/list axis values through item_axis_policy",
)
def smoke_row_assembly_policy() -> None:
context = {
"subject": "configured cast",
"subject_phrase": "configured adult cast",
"age": "21+ adults",
"body": "varied adult builds",
"body_phrase": "varied adult builds",
"figure": "balanced cast",
"skin": "warm skin tones",
"hair": "dark hair",
"eyes": "brown eyes",
"cast_summary": "one woman and one man",
"scene_kind": "configured_cast",
"women_count": "1",
"men_count": "1",
"person_count": "2",
}
count_adjustment = {"requested_women_count": 1, "requested_men_count": 1}
kwargs = {
"row_number": 2,
"start_index": 10,
"category": {"name": "Axis Test", "slug": "axis_test"},
"subcategory": {
"name": "Custom Scene",
"slug": "custom_scene",
"prompt_template": "Scene: {item}. Composition: {composition_prompt}. Avoid: {negative_prompt}.",
"caption_template": "{trigger}, {item}, {scene}",
},
"item": {"text": "shared structured action"},
"context": context,
"subject_type": "configured_cast",
"item_text": "shared structured action",
"item_name": "shared_action",
"item_axis_values": {"action_family": "test_action"},
"item_template_metadata": {"position_key": "test_position"},
"formatter_hints": {"krea": ["test_hint"]},
"item_label": "Scene",
"style": "clean test style",
"positive_suffix": "clear readable composition.",
"negative_prompt": "bad anatomy",
"scene_slug": "test_room",
"scene": "warm test room",
"scene_entry": {"slug": "test_room", "prompt": "warm test room", "theme": "fixture_theme"},
"pose": "standing close",
"expression": "focused look",
"shared_expression": "focused look",
"character_expressions": ["Woman A has focused look"],
"character_expression_text": "Woman A has focused look",
"expression_disabled": True,
"expression_intensity": 0.7,
"expression_intensity_source": "disabled",
"composition": "centered frame",
"source_composition": "centered frame",
"composition_entry": {"prompt": "centered frame"},
"role_graph": "the visible partner stays centered",
"source_role_graph": "Man A stays centered",
"action_family": "test_action",
"position_family": "standing",
"position_key": "test_position",
"position_keys": ["test_position"],
"pov_character_labels": ["Man A"],
"cast_descriptors": ["Woman A: adult woman", "Man A: adult man"],
"cast_descriptor_text": "Woman A: adult woman; Man A: adult man",
"seed_config": {"content_seed": 123},
"hardcore_position_config": {"family": "standing"},
"location_config": {"location": "test_room", "theme": "fixture_theme", "apply_mode": "replace"},
"composition_config": {"composition": "centered", "theme": "fixture_theme"},
"content_seed_axis": "pose",
"count_adjustment": count_adjustment,
"applied_profile": {"name": "profile_a"},
"profile_status": "applied",
"applied_slot": {"label": "Woman A"},
"slot_status": "applied",
"character_slots": [{"label": "Woman A"}, {"label": "Man A"}],
}
request = row_assembly.CustomRowAssemblyRequest(**kwargs)
row = row_assembly.assemble_custom_row(request)
delegated = pb._assemble_custom_row(request)
_expect(row == delegated, "Prompt builder row assembly wrapper should delegate without changing output")
_expect(request.content_seed_axis == "pose", "Row assembly request lost seed-axis metadata")
_expect(row["id"] == "sxcp_0011", "Row assembly changed row indexing")
_expect(row["batch"] == "batch_001", "Row assembly changed batch calculation")
_expect(row["source"] == "json_category", "Row assembly lost source marker")
_expect(row["figure"] == "balanced cast", "Row assembly lost figure metadata")
_expect(row["formatter_hints"] == {"krea": ["test_hint"]}, "Row assembly lost formatter hints")
_expect(row["scene_entry"].get("slug") == "test_room", "Row assembly lost selected scene entry")
_expect(row["location_theme"] == "fixture_theme", "Row assembly lost location theme")
_expect(row["scene_theme"] == "fixture_theme", "Row assembly lost selected scene theme")
_expect(row["composition_entry"].get("prompt") == "centered frame", "Row assembly lost selected composition entry")
_expect(row["composition_theme"] == "fixture_theme", "Row assembly lost composition theme")
_expect(row["cast_count_adjustment"] == count_adjustment, "Row assembly lost configured-cast count adjustment")
_expect(row["content_seed_axis"] == "pose", "Row assembly lost content seed axis")
_expect("POV participant: Man A" in row["prompt"], "Row assembly lost POV prompt directive")
_expect("Characters: Woman A: adult woman; Man A: adult man." in row["prompt"], "Row assembly lost cast descriptor insertion")
_expect(row["caption"].endswith("Woman A: adult woman; Man A: adult man"), "Row assembly lost caption descriptor append")
_expect(row["expression"] == "", "Disabled expression should clear row expression")
_expect(row["expression_enabled"] is False, "Disabled expression should mark expression disabled")
_expect(row["expression_intensity"] is None, "Disabled expression should clear intensity")
_expect(row["expression_intensity_source"] == "disabled", "Disabled expression should preserve disabled source")
def smoke_formatter_input_policy() -> None:
source_row = {
"prompt": "A simple adult portrait. Setting: quiet studio. Pose: standing calmly. Avoid: low quality.",
"caption": "adult portrait, quiet studio",
"negative_prompt": "low quality",
"subject_type": "woman",
"primary_subject": "woman",
"age": "25-year-old adult",
"body_phrase": "average figure",
"skin": "warm skin",
"hair": "dark hair",
"eyes": "brown eyes",
"item": "black dress",
"scene_text": "quiet studio",
"pose": "standing calmly",
"composition": "centered portrait",
"trigger": Trigger,
}
source_json = _json(source_row)
_expect(
formatter_input.input_hint_choices(text_hint=formatter_input.INPUT_HINT_PROMPT) == ["auto", "metadata_json", "prompt"],
"Formatter prompt input-hint choices changed",
)
_expect(
formatter_input.input_hint_choices(text_hint=formatter_input.INPUT_HINT_CAPTION_OR_PROMPT)
== ["auto", "metadata_json", "caption_or_prompt"],
"Formatter caption input-hint choices changed",
)
_expect(
formatter_input.normalize_input_hint("bad_hint") == "auto",
"Formatter input-hint policy should normalize invalid values to auto",
)
_expect(
formatter_input.normalize_input_hint("caption", text_hint=formatter_input.INPUT_HINT_CAPTION_OR_PROMPT)
== "caption_or_prompt",
"Formatter input-hint policy lost caption alias",
)
_expect(
formatter_input.normalize_input_hint("caption_or_prompt", text_hint=formatter_input.INPUT_HINT_PROMPT) == "prompt",
"Formatter input-hint policy should map text hints to the route's text mode",
)
row, method = formatter_input.row_from_inputs(source_json, "", "auto")
_expect(method == "source_json", "Formatter input parser should read source JSON when metadata is empty")
_expect(row == source_row, "Formatter input parser changed parsed JSON row")
row, method = formatter_input.row_from_inputs(source_json, "", "bad_hint")
_expect(method == "source_json" and row == source_row, "Formatter input parser should treat invalid hints as auto")
row, method = formatter_input.row_from_inputs(source_json, "", "prompt")
_expect(row is None and method == "text", "Formatter input parser should not parse source JSON in explicit prompt mode")
pair_metadata = {
"trigger": Trigger,
"softcore_row": {
"prompt": f"{Trigger}, {Trigger}, embedded-only soft.",
"caption": f"{Trigger}, {Trigger}, embedded-only soft caption.",
"negative_prompt": "bad anatomy, bad anatomy",
"softcore_partner_styling": {"outfits": ["row partner outfit"], "pose": "row partner pose"},
"camera_config": {"camera_mode": "standard"},
"camera_directive": "Camera: row soft front view.",
"camera_scene_directive": "Row soft scene camera layout.",
},
"hardcore_row": {
"prompt": f"{Trigger}, {Trigger}, embedded-only hard.",
"caption": f"{Trigger}, {Trigger}, embedded-only hard caption.",
"negative_prompt": "low quality, low quality",
"hardcore_clothing_state": "row hard clothing state",
"camera_config": {"camera_mode": "pov"},
"camera_scene_directive": "Row hard scene camera layout.",
},
}
_expect(formatter_input.is_pair_metadata(pair_metadata), "Formatter input policy should detect structural pair metadata")
parsed_pair, pair_method = formatter_input.row_from_inputs("", _json(pair_metadata), "metadata_json")
_expect(pair_method == "metadata_json", "Formatter input parser should read pair metadata JSON")
_expect(formatter_input.is_pair_metadata(parsed_pair), "Formatter input parser should preserve structural pair metadata")
_expect_trigger_once("formatter_input.pair.soft_prompt", parsed_pair.get("softcore_prompt"), Trigger)
_expect(
parsed_pair.get("softcore_partner_styling") == parsed_pair["softcore_row"].get("softcore_partner_styling"),
"Formatter input parser did not normalize pair soft side metadata",
)
_expect(
parsed_pair.get("hardcore_clothing_state") == parsed_pair["hardcore_row"].get("hardcore_clothing_state"),
"Formatter input parser did not normalize pair hard side metadata",
)
_expect(
parsed_pair.get("softcore_camera_config") == parsed_pair["softcore_row"].get("camera_config"),
"Formatter input parser did not normalize pair camera metadata",
)
_expect_no_duplicate_comma_items("formatter_input.pair.hard_negative", parsed_pair.get("hardcore_negative_prompt"))
_expect(formatter_input.split_avoid("Prompt body. Avoid: blur, watermark") == ("Prompt body", "blur, watermark"), "Avoid split changed")
_expect(
formatter_input.prompt_field(source_row["prompt"], "Setting") == "quiet studio",
"Prompt field extraction changed",
)
_expect(
formatter_input.row_value({"prompt": source_row["prompt"]}, "scene_text", ("Setting",)) == "quiet studio",
"Row value prompt fallback changed",
)
_expect(
krea_formatter.PROMPT_FIELD_LABELS is formatter_input.DEFAULT_PROMPT_FIELD_LABELS,
"Krea formatter field-label inventory should delegate to formatter_input",
)
_expect(
sdxl_formatter.PROMPT_FIELD_LABELS is formatter_input.DEFAULT_PROMPT_FIELD_LABELS,
"SDXL formatter field-label inventory should delegate to formatter_input",
)
_expect(
caption_naturalizer.PROMPT_FIELD_LABELS is formatter_input.DEFAULT_PROMPT_FIELD_LABELS,
"Caption formatter field-label inventory should delegate to formatter_input",
)
labeled_prompt = "Sexual scene: close-contact action. Camera control: side view. Composition: centered frame."
_expect(
formatter_input.prompt_field(labeled_prompt, "Sexual scene") == "close-contact action",
"Shared formatter field-label inventory lost Sexual scene parsing",
)
_expect(
formatter_input.prompt_field(labeled_prompt, "Camera control") == "side view",
"Shared formatter field-label inventory lost Camera control parsing",
)
stripped_labels = formatter_input.strip_prompt_field_labels(
"Characters: woman. Erotic outfit: sheer dress. Camera: side view."
)
_expect("Characters:" not in stripped_labels, "Shared label stripper did not remove Characters label")
_expect("Erotic outfit:" not in stripped_labels, "Shared label stripper did not remove Erotic outfit label")
_expect("Camera:" not in stripped_labels, "Shared label stripper did not remove Camera label")
_expect(krea_formatter._clean("a b , c") == formatter_input.clean_text("a b , c"), "Krea clean helper is not delegated")
_expect(sdxl_formatter._clean("a b , c") == formatter_input.clean_text("a b , c"), "SDXL clean helper is not delegated")
_expect(
sdxl_formatter._strip_prompt_field_labels("Characters: woman. Camera: side view.") == "woman. side view.",
"SDXL label stripper should delegate to formatter_input",
)
_expect(caption_naturalizer._clean_text("a b , c") == formatter_input.clean_text("a b , c"), "Caption clean helper is not delegated")
_expect(krea_formatter._strip_trigger(f"{Trigger}, prompt text", False) == "prompt text", "Krea trigger stripping changed")
_expect(sdxl_formatter._strip_trigger(f"{SdxlTrigger}, prompt text", False) == "prompt text", "SDXL trigger stripping changed")
_expect(caption_naturalizer._remove_trigger(Trigger, Trigger) == "", "Caption exact-trigger removal changed")
krea = krea_formatter.format_krea2_prompt(source_json, input_hint="auto")
sdxl = sdxl_formatter.format_sdxl_prompt(source_json, input_hint="auto", trigger=SdxlTrigger, prepend_trigger=True)
caption, caption_method = caption_naturalizer.naturalize_caption(source_json, input_hint="auto", trigger=Trigger)
_expect(krea.get("method", "").startswith("source_json:krea2("), "Krea formatter did not use shared source JSON parsing")
_expect(sdxl.get("method", "").startswith("source_json:sdxl("), "SDXL formatter did not use shared source JSON parsing")
_expect(caption_method.startswith("source_json:metadata("), "Caption naturalizer did not use shared source JSON parsing")
_expect_text("formatter_input.krea_prompt", krea.get("krea_prompt"), 20)
_expect_text("formatter_input.sdxl_prompt", sdxl.get("sdxl_prompt"), 20)
_expect_text("formatter_input.caption", caption, 20)
bad_hint_krea = krea_formatter.format_krea2_prompt(source_json, input_hint="bad_hint")
bad_hint_sdxl = sdxl_formatter.format_sdxl_prompt(
source_json,
input_hint="bad_hint",
trigger=SdxlTrigger,
prepend_trigger=True,
)
bad_hint_caption, bad_hint_caption_method = caption_naturalizer.naturalize_caption(
source_json,
input_hint="bad_hint",
trigger=Trigger,
)
_expect(
bad_hint_krea.get("method", "").startswith("source_json:krea2("),
"Krea formatter did not normalize bad input hint to auto",
)
_expect(
bad_hint_sdxl.get("method", "").startswith("source_json:sdxl("),
"SDXL formatter did not normalize bad input hint to auto",
)
_expect(
bad_hint_caption_method.startswith("source_json:metadata("),
"Caption formatter did not normalize bad input hint to auto",
)
fallback_sdxl = sdxl_formatter.format_sdxl_prompt(
"Characters: woman. Erotic outfit: sheer dress. Camera: side view. Avoid: blur",
input_hint="prompt",
style_preset="none",
quality_preset="none",
trigger=SdxlTrigger,
prepend_trigger=False,
)
fallback_prompt = fallback_sdxl.get("sdxl_prompt", "")
_expect("Characters:" not in fallback_prompt, "SDXL fallback leaked Characters label")
_expect("Erotic outfit:" not in fallback_prompt, "SDXL fallback leaked Erotic outfit label")
_expect("Camera:" not in fallback_prompt, "SDXL fallback leaked Camera label")
_expect("blur" in fallback_sdxl.get("negative_prompt", ""), "SDXL fallback lost Avoid negative text")
def smoke_formatter_target_policy() -> None:
_expect(
formatter_target.target_choices() == ["auto", "single", "softcore", "hardcore"],
"Formatter target choices changed",
)
_expect(formatter_target.normalize_target("single") == "single", "Formatter target lost single")
_expect(formatter_target.normalize_target("Hard-Core") == "hardcore", "Formatter target alias lost hardcore")
_expect(formatter_target.normalize_target("soft") == "softcore", "Formatter target alias lost softcore")
_expect(formatter_target.normalize_target("bad target") == "auto", "Formatter target should normalize invalid values")
auto_pair = formatter_target.pair_policy("auto")
_expect(auto_pair.target == "auto", "Pair target policy lost normalized auto target")
_expect(auto_pair.pair_target == "auto", "Pair target policy lost auto pair target")
_expect(auto_pair.selected_side == "softcore", "Pair auto should select softcore side for single-output formatters")
_expect(auto_pair.include_softcore and auto_pair.include_hardcore, "Pair auto should include both sides for combined captions")
single_pair = formatter_target.pair_policy("single")
_expect(single_pair.target == "single", "Pair target policy lost normalized single target")
_expect(single_pair.pair_target == "auto", "Pair single should map to auto for pair inclusion")
_expect(single_pair.selected_side == "softcore", "Pair single should select softcore side by default")
_expect(single_pair.include_softcore and single_pair.include_hardcore, "Pair single should include both sides when treated as auto")
hard_pair = formatter_target.pair_policy("hard")
_expect(hard_pair.target == "hardcore", "Pair target policy lost hard alias")
_expect(hard_pair.pair_target == "hardcore", "Pair hard alias should become hardcore pair target")
_expect(hard_pair.selected_side == "hardcore", "Pair hardcore should select hardcore side")
_expect(not hard_pair.include_softcore and hard_pair.include_hardcore, "Pair hardcore should include only hard side")
def smoke_formatter_detail_policy() -> None:
_expect(
formatter_detail.detail_level_choices() == ["balanced", "concise", "dense"],
"Formatter detail choices changed",
)
_expect(formatter_detail.normalize_detail_level("dense") == "dense", "Formatter detail lost dense")
_expect(formatter_detail.normalize_detail_level("BAD") == "balanced", "Formatter detail should normalize invalid values")
_expect(formatter_detail.detail_allows("concise") is False, "Formatter detail concise gate changed")
_expect(formatter_detail.detail_allows("dense", dense_only=True) is True, "Formatter detail dense-only gate changed")
_expect(caption_policy.DETAIL_LEVELS is formatter_detail.DETAIL_LEVELS, "Caption detail levels should delegate")
_expect(
caption_policy.normalize_detail_level("bad") == formatter_detail.normalize_detail_level("bad"),
"Caption detail normalization should delegate",
)
_expect(
caption_policy.detail_allows("dense", dense_only=True) == formatter_detail.detail_allows("dense", dense_only=True),
"Caption detail gate should delegate",
)
def smoke_krea_format_route_policy() -> None:
row = _prompt_row(
name="krea_format_route_single",
category="woman",
subcategory="random",
seed=3601,
men_count=0,
camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
)
single_request = krea_format_route.KreaFormatRequest(
source_text="",
metadata_json=_json(row),
target="single",
detail_level="dense",
style_mode="photographic",
extra_positive="krea route marker",
extra_negative="krea route negative",
)
typed_single = krea_format_route.format_krea2_prompt_result(
single_request,
krea_formatter._krea_format_dependencies(),
)
public_single = krea_formatter.format_krea2_prompt(
"",
metadata_json=single_request.metadata_json,
target=single_request.target,
detail_level=single_request.detail_level,
style_mode=single_request.style_mode,
extra_positive=single_request.extra_positive,
extra_negative=single_request.extra_negative,
)
_expect(typed_single.output == public_single, "Typed Krea format route should match public single formatter output")
_expect(typed_single.branch == "metadata(single)", "Typed Krea format route changed single branch")
_expect(typed_single.target == "single", "Typed Krea format route lost target normalization")
single_trace = json.loads(_expect_text("krea_format_route_policy.single_trace", typed_single.output.get("route_trace_json"), 20))
_expect(single_trace.get("formatter") == "krea2", "Typed Krea single trace lost formatter")
_expect(single_trace.get("branch") == "metadata(single)", "Typed Krea single trace lost branch")
_expect(single_trace.get("target") == "single", "Typed Krea single trace lost target")
_expect("krea route marker" in typed_single.output.get("krea_prompt", ""), "Typed Krea route lost extra positive")
_expect("krea route negative" in typed_single.output.get("negative_prompt", ""), "Typed Krea route lost extra negative")
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3602,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
)
pair_request = krea_format_route.KreaFormatRequest(
source_text="",
metadata_json=_json(pair),
target="hardcore",
detail_level="balanced",
style_mode="preserve",
extra_positive="pair route marker",
extra_negative="pair route negative",
)
typed_pair = krea_format_route.format_krea2_prompt_result(
pair_request,
krea_formatter._krea_format_dependencies(),
)
public_pair = krea_formatter.format_krea2_prompt(
"",
metadata_json=pair_request.metadata_json,
target=pair_request.target,
detail_level=pair_request.detail_level,
style_mode=pair_request.style_mode,
extra_positive=pair_request.extra_positive,
extra_negative=pair_request.extra_negative,
)
_expect(typed_pair.output == public_pair, "Typed Krea format route should match public pair formatter output")
_expect(typed_pair.branch == "insta_of_pair", "Typed Krea format route changed pair branch")
pair_trace = json.loads(_expect_text("krea_format_route_policy.pair_trace", typed_pair.output.get("route_trace_json"), 20))
_expect(pair_trace.get("branch") == "insta_of_pair", "Typed Krea pair trace lost branch")
_expect(pair_trace.get("selected_side") == "hardcore", "Typed Krea pair trace lost selected side")
_expect_text("krea_format_route_policy.hard_prompt", typed_pair.output.get("krea_hardcore_prompt"), 40)
_expect("pair route marker" in typed_pair.output.get("krea_prompt", ""), "Typed Krea pair route lost extra positive")
fallback_request = krea_format_route.KreaFormatRequest(
source_text="Scene: quiet studio. Pose: seated portrait. Avoid: blur",
input_hint="prompt",
target="weird",
detail_level="verbose",
style_mode="invalid",
preserve_trigger=False,
)
typed_fallback = krea_format_route.format_krea2_prompt_result(
fallback_request,
krea_formatter._krea_format_dependencies(),
)
public_fallback = krea_formatter.format_krea2_prompt(
fallback_request.source_text,
input_hint=fallback_request.input_hint,
target=fallback_request.target,
detail_level=fallback_request.detail_level,
style_mode=fallback_request.style_mode,
preserve_trigger=fallback_request.preserve_trigger,
)
_expect(typed_fallback.output == public_fallback, "Typed Krea format route should match public fallback output")
_expect(typed_fallback.branch == "fallback", "Typed Krea format route changed fallback branch")
_expect(typed_fallback.target == "auto", "Typed Krea format route should normalize invalid target")
fallback_trace = json.loads(_expect_text("krea_format_route_policy.fallback_trace", typed_fallback.output.get("route_trace_json"), 20))
_expect(fallback_trace.get("branch") == "fallback", "Typed Krea fallback trace lost branch")
_expect(fallback_trace.get("input_hint") == "prompt", "Typed Krea fallback trace lost input hint")
_expect(typed_fallback.detail_level == "balanced", "Typed Krea format route should normalize invalid detail level")
_expect(typed_fallback.style_mode == "preserve", "Typed Krea format route should normalize invalid style mode")
_expect(krea_format_route.style_mode_choices() == ["preserve", "photographic", "minimal"], "Krea style mode choices changed")
_expect(krea_format_route.normalize_style_mode("photographic") == "photographic", "Krea style mode lost photographic")
_expect(krea_format_route.normalize_style_mode("bad") == "preserve", "Krea style mode invalid fallback changed")
_expect("blur" in typed_fallback.output.get("negative_prompt", ""), "Typed Krea fallback route lost Avoid negative")
def smoke_formatter_cast_policy() -> None:
descriptor = (
"Woman A / primary creator: 25-year-old adult woman, average figure, warm skin, dark hair; "
"Man A: 40-year-old adult man, average figure, tan skin, short dark hair"
)
entries = [
("Woman A", "25-year-old adult woman, average figure, warm skin, dark hair"),
("Man A", "40-year-old adult man, average figure, tan skin, short dark hair"),
]
_expect(krea_cast.cast_entries(descriptor) == entries, "Shared cast entry parser changed")
_expect(caption_naturalizer._cast_entries(descriptor) == entries, "Caption cast parser should delegate to shared cast policy")
_expect(krea_cast.cast_labels(descriptor) == ["Woman A", "Man A"], "Shared cast label parser changed")
_expect(
caption_naturalizer._cast_labels(descriptor) == krea_cast.cast_labels(descriptor),
"Caption cast labels should delegate to shared cast policy",
)
natural = krea_cast.natural_cast_descriptor_text(descriptor)
_expect(natural.startswith("A 25-year-old adult woman"), "Shared natural cast descriptor text changed")
_expect(caption_naturalizer._natural_cast_descriptor_text(descriptor) == natural, "Caption cast descriptor text should delegate")
_expect(
krea_cast.natural_label_text("Woman A faces Man A.", ["Woman A", "Man A"]) == "The woman faces the man.",
"Krea natural label text should keep sentence capitalization",
)
_expect(
caption_naturalizer._natural_label_text("Woman A faces Man A.", ["Woman A", "Man A"]) == "the woman faces the man.",
"Caption natural label text should preserve previous lowercase inline behavior",
)
def smoke_caption_policy() -> None:
_expect(
caption_naturalizer.STYLE_TAILS is caption_policy.STYLE_TAILS,
"Caption naturalizer style tails should delegate to caption_policy",
)
_expect(
caption_naturalizer.ITEM_LABELS is caption_policy.ITEM_LABELS,
"Caption naturalizer item labels should delegate to caption_policy",
)
_expect(
caption_naturalizer.ACTION_FAMILY_CAPTION_LABELS is caption_policy.ACTION_FAMILY_CAPTION_LABELS,
"Caption naturalizer action labels should delegate to caption_policy",
)
_expect(caption_policy.normalize_detail_level("bad") == "balanced", "Caption invalid detail fallback changed")
_expect(caption_policy.normalize_style_policy("bad") == "drop_style_tail", "Caption invalid style fallback changed")
_expect(
caption_policy.normalize_style_policy("Keep Style Terms") == "keep_style_terms",
"Caption style policy should normalize spaces/case",
)
_expect(caption_policy.style_policy_choices() == ["drop_style_tail", "keep_style_terms"], "Caption style policy choices changed")
_expect(caption_policy.keep_style_terms("keep_style_terms") is True, "Caption style policy keep flag changed")
_expect(caption_policy.detail_allows("concise") is False, "Caption concise detail gate changed")
_expect(caption_policy.detail_allows("dense", dense_only=True) is True, "Caption dense-only gate changed")
_expect("training_concise" in caption_policy.caption_profile_choices(), "Caption profile choices lost training_concise")
_expect(
caption_policy.normalize_caption_profile("bad") == caption_policy.CAPTION_PROFILE_DEFAULT,
"Caption invalid profile fallback changed",
)
_expect(
caption_policy.normalize_caption_profile("training-dense") == "training_dense",
"Caption profile should normalize hyphen spelling",
)
_expect(
caption_policy.apply_caption_profile(
"training_dense",
detail_level="concise",
style_policy="keep_style_terms",
include_trigger=False,
)
== ("dense", "drop_style_tail", True),
"Caption training_dense profile overrides changed",
)
_expect(
caption_policy.apply_caption_profile(
"manual_controls",
detail_level="concise",
style_policy="keep_style_terms",
include_trigger=False,
)
== ("concise", "keep_style_terms", False),
"Caption manual profile should preserve explicit controls",
)
style_tail = caption_policy.STYLE_TAILS[0]
_expect(
caption_policy.strip_style_tail(f"caption body{style_tail}") == "caption body",
"Caption style-tail stripping changed",
)
_expect(
caption_naturalizer._strip_style_tail(f"caption body{style_tail}") == "caption body",
"Caption naturalizer style-tail wrapper should delegate",
)
_expect(
caption_policy.normalize_composition("vertical centered body frame") == "centered body frame",
"Caption composition normalization changed",
)
_expect(
caption_policy.normalize_composition("vertical centered body frame composition") == "centered body frame",
"Caption composition should drop trailing composition label",
)
_expect(
caption_policy.clean_clothing("silk dress, fashion editorial styling") == "silk dress",
"Caption clothing cleanup changed",
)
row = {"action_family": "oral", "position_family": ""}
_expect(caption_policy.metadata_action_label(row) == "oral action", "Caption action-family label changed")
row = {"action_family": "anal", "position_family": ""}
_expect(caption_policy.metadata_action_label(row) == "anal action", "Caption anal action-family label changed")
row = {"action_family": "manual", "position_family": ""}
_expect(caption_policy.metadata_action_label(row) == "manual action", "Caption manual action-family label changed")
row = {"action_family": "threesome", "position_family": ""}
_expect(caption_policy.metadata_action_label(row) == "three-person action", "Caption threesome action-family label changed")
row = {"action_family": "group", "position_family": ""}
_expect(caption_policy.metadata_action_label(row) == "group action", "Caption group action-family label changed")
row = {"action_family": "oral", "position_family": "Anal"}
_expect(caption_naturalizer._metadata_action_label(row) == "anal action", "Caption position-family label priority changed")
browsing_caption, browsing_method = caption_naturalizer.naturalize_caption(
"woman, red dress, studio",
caption_profile="browsing",
include_trigger=True,
)
_expect(not browsing_caption.startswith(Trigger), "Caption browsing profile should disable trigger by default")
_expect(browsing_method == "text(fallback)", "Caption browsing profile changed fallback method")
def smoke_caption_format_route_policy() -> None:
row = _prompt_row(
name="caption_format_route_single",
category="woman",
subcategory="random",
seed=3801,
men_count=0,
)
metadata_request = caption_format_route.CaptionFormatRequest(
source_text="",
metadata_json=_json(row),
input_hint="metadata_json",
target="single",
trigger=Trigger,
include_trigger=False,
detail_level="concise",
style_policy="keep_style_terms",
caption_profile="training_dense",
)
typed_metadata = caption_format_route.naturalize_caption_result(
metadata_request,
caption_naturalizer._caption_format_dependencies(),
)
public_metadata = caption_naturalizer.naturalize_caption(
"",
metadata_json=metadata_request.metadata_json,
input_hint=metadata_request.input_hint,
target=metadata_request.target,
trigger=metadata_request.trigger,
include_trigger=metadata_request.include_trigger,
detail_level=metadata_request.detail_level,
style_policy=metadata_request.style_policy,
caption_profile=metadata_request.caption_profile,
)
_expect(typed_metadata.as_tuple() == public_metadata, "Typed caption format route should match public metadata output")
_expect(typed_metadata.branch == "metadata", "Typed caption format route changed metadata branch")
_expect(typed_metadata.input_hint == "metadata_json", "Typed caption route lost input hint")
_expect(typed_metadata.target == "single", "Typed caption route lost target normalization")
_expect(typed_metadata.detail_level == "dense", "Typed caption route lost training_dense detail override")
_expect(typed_metadata.style_policy == "drop_style_tail", "Typed caption route lost training_dense style override")
_expect(typed_metadata.include_trigger is True, "Typed caption route lost training_dense trigger override")
metadata_trace = json.loads(_expect_text("caption_format_route_policy.metadata_trace", typed_metadata.route_trace_json, 20))
_expect(metadata_trace.get("formatter") == "caption", "Typed caption metadata trace lost formatter")
_expect(metadata_trace.get("branch") == "metadata", "Typed caption metadata trace lost branch")
_expect(metadata_trace.get("target") == "single", "Typed caption metadata trace lost target")
traced_public_metadata = caption_naturalizer.naturalize_caption_with_trace(
"",
metadata_json=metadata_request.metadata_json,
input_hint=metadata_request.input_hint,
target=metadata_request.target,
trigger=metadata_request.trigger,
include_trigger=metadata_request.include_trigger,
detail_level=metadata_request.detail_level,
style_policy=metadata_request.style_policy,
caption_profile=metadata_request.caption_profile,
)
_expect(traced_public_metadata == typed_metadata.as_trace_tuple(), "Caption trace wrapper drifted from typed route")
_expect(typed_metadata.caption.startswith(Trigger), "Typed caption metadata route should prepend training trigger")
fallback_request = caption_format_route.CaptionFormatRequest(
source_text="woman, red dress, studio, coloured pencil comic illustration",
input_hint="bad_hint",
target="weird",
trigger=Trigger,
include_trigger=True,
detail_level="dense",
style_policy="drop_style_tail",
caption_profile="browsing",
)
typed_fallback = caption_format_route.naturalize_caption_result(
fallback_request,
caption_naturalizer._caption_format_dependencies(),
)
public_fallback = caption_naturalizer.naturalize_caption(
fallback_request.source_text,
input_hint=fallback_request.input_hint,
target=fallback_request.target,
trigger=fallback_request.trigger,
include_trigger=fallback_request.include_trigger,
detail_level=fallback_request.detail_level,
style_policy=fallback_request.style_policy,
caption_profile=fallback_request.caption_profile,
)
_expect(typed_fallback.as_tuple() == public_fallback, "Typed caption format route should match public fallback output")
_expect(typed_fallback.branch == "text", "Typed caption format route changed fallback branch")
_expect(typed_fallback.input_hint == "auto", "Typed caption route should normalize invalid input hint")
_expect(typed_fallback.target == "auto", "Typed caption route should normalize invalid target")
_expect(typed_fallback.include_trigger is False, "Typed caption browsing profile should disable trigger")
fallback_trace = json.loads(_expect_text("caption_format_route_policy.fallback_trace", typed_fallback.route_trace_json, 20))
_expect(fallback_trace.get("branch") == "text", "Typed caption fallback trace lost branch")
_expect(fallback_trace.get("input_hint") == "auto", "Typed caption fallback trace lost input hint")
_expect(typed_fallback.keep_style is True, "Typed caption browsing profile should keep style terms")
_expect(not typed_fallback.caption.startswith(Trigger), "Typed caption fallback route should not prepend browsing trigger")
_expect(typed_fallback.method == "text(fallback)", "Typed caption fallback method changed")
def smoke_caption_text_policy() -> None:
row = {
"primary_subject": "woman",
"age_band": "25-year-old adult",
"body_phrase": "slim figure",
"caption": f"{Trigger}, woman, 25-year-old adult, slim figure, fair skin, blonde hair, blue eyes, studio",
"formatter_hints": {"caption": ["caption policy hint"]},
}
_expect(
caption_naturalizer._body_phrase("slim", "balanced figure") == caption_text_policy.body_phrase("slim", "balanced figure"),
"Caption body phrase wrapper should delegate to caption_text_policy",
)
_expect(
caption_naturalizer._single_caption_front(row) == caption_text_policy.single_caption_front(row),
"Caption front parser wrapper should delegate to caption_text_policy",
)
_expect(
caption_naturalizer._formatter_hint_parts(row) == caption_text_policy.formatter_hint_parts(row),
"Caption formatter hint wrapper should delegate to caption_text_policy",
)
_expect(
caption_naturalizer._append_formatter_hints("Base sentence.", row)
== caption_text_policy.append_formatter_hints("Base sentence.", row),
"Caption formatter hint append wrapper should delegate to caption_text_policy",
)
_expect(
caption_naturalizer._with_trigger("A caption body", Trigger, True)
== caption_text_policy.with_trigger("A caption body", Trigger, True),
"Caption trigger wrapper should delegate to caption_text_policy",
)
axis_detail_row = {
"item_axis_values": {
"position": "standing oral position",
"contact_detail": "mouth contact at hip height",
"duplicate": "standing oral position",
"ignored": "random",
}
}
_expect(
caption_text_policy.item_axis_detail_text(axis_detail_row, "generic action")
== "standing oral position and mouth contact at hip height",
"Caption axis detail text should flatten selected item axes",
)
_expect(
caption_text_policy.item_axis_detail_text(axis_detail_row, "standing oral position already appears")
== "mouth contact at hip height",
"Caption axis detail text should skip details already present in item prose",
)
deps = caption_naturalizer._caption_metadata_route_dependencies()
_expect(deps.clean_text is caption_text_policy.clean_text, "Caption route deps lost clean text policy")
_expect(deps.field_row_value is caption_text_policy.field_row_value, "Caption route deps lost field row-value policy")
_expect(deps.expression_disabled is caption_text_policy.expression_disabled, "Caption route deps lost expression policy")
_expect(deps.single_caption_front is caption_text_policy.single_caption_front, "Caption route deps lost front parser")
_expect(deps.item_axis_detail_text is caption_text_policy.item_axis_detail_text, "Caption route deps lost item-axis detail policy")
_expect(deps.metadata_to_prose is caption_naturalizer._metadata_to_prose, "Caption route deps lost metadata recursion callback")
def _expect_caption_route_parity(
name: str,
row: dict[str, Any],
route_builder: Callable[
[caption_metadata_routes.CaptionMetadataRouteRequest, caption_metadata_routes.CaptionMetadataRouteDependencies],
caption_metadata_routes.CaptionMetadataRoute | None,
],
wrapper: Callable[[dict[str, Any], str, bool], tuple[str, str] | None],
expected_method: str,
) -> None:
request = caption_naturalizer._caption_metadata_route_request(row, "balanced", False)
deps = caption_naturalizer._caption_metadata_route_dependencies()
typed_route = route_builder(request, deps)
legacy_route = wrapper(row, "balanced", False)
_expect(typed_route is not None, f"{name} typed caption metadata route did not match")
assert typed_route is not None
_expect(
typed_route.as_tuple() == legacy_route,
f"{name} typed caption metadata route should match legacy wrapper output",
)
_expect(typed_route.method == expected_method, f"{name} caption route method changed")
_expect_text(f"{name}.caption_route", typed_route.prose, 20)
def smoke_caption_metadata_routes() -> None:
single = {
"primary_subject": "woman",
"age_band": "25-year-old adult",
"body_phrase": "slim figure",
"skin": "fair skin",
"hair": "long blonde hair",
"eyes": "blue eyes",
"item": "silk dress",
"pose": "standing beside a window",
"scene_text": "quiet studio with warm daylight",
"expression": "soft smile",
"composition": "vertical centered portrait",
}
_expect_caption_route_parity(
"caption_route_single",
single,
caption_metadata_routes.single_from_row_result,
caption_naturalizer._single_from_row,
"metadata(single)",
)
couple = {
"primary_subject": "a woman and a man",
"subject_phrase": "woman and man",
"age": "25-year-old adult and 40-year-old adult",
"body": "slim and average builds",
"item": "Partner A wears black dress; Partner B wears dark shirt",
"pose": "standing close together",
"scene_text": "private lounge with soft lamps",
"expression": "shared confident gaze",
"composition": "two-person editorial frame",
}
_expect_caption_route_parity(
"caption_route_couple",
couple,
caption_metadata_routes.couple_from_row_result,
caption_naturalizer._couple_from_row,
"metadata(couple)",
)
configured = _fixture_hardcore_row()
_expect_caption_route_parity(
"caption_route_configured_cast",
configured,
caption_metadata_routes.configured_cast_from_row_result,
caption_naturalizer._configured_cast_from_row,
"metadata(configured_cast)",
)
configured_character_expression = _fixture_hardcore_row(
character_expression_text="Woman A has flushed focus; Man A has concentrated stare",
)
character_expression_route = caption_metadata_routes.configured_cast_from_row_result(
caption_naturalizer._caption_metadata_route_request(configured_character_expression, "balanced", False),
caption_naturalizer._caption_metadata_route_dependencies(),
)
_expect(character_expression_route is not None, "Caption configured-cast character expression row did not match")
assert character_expression_route is not None
_expect(
"with Woman A has" not in character_expression_route.prose,
"Caption configured-cast prose kept old character-expression grammar",
)
_expect(
"Woman A with flushed focus" in character_expression_route.prose,
"Caption configured-cast prose did not naturalize Woman A expression",
)
_expect(
"Man A with concentrated stare" in character_expression_route.prose,
"Caption configured-cast prose did not naturalize Man A expression",
)
configured_axis_only = _fixture_hardcore_row(
item="generic configured adult action",
role_graph="",
source_role_graph="",
item_axis_values={
"position": "standing oral position",
"contact_detail": "mouth contact at hip height, hands on hips",
},
action_family="oral",
position_family="oral",
position_key="standing",
position_keys=["standing"],
)
axis_route = caption_metadata_routes.configured_cast_from_row_result(
caption_naturalizer._caption_metadata_route_request(configured_axis_only, "balanced", False),
caption_naturalizer._caption_metadata_route_dependencies(),
)
_expect(axis_route is not None, "Caption configured-cast axis-only row did not match")
assert axis_route is not None
_expect("Selected action details include" in axis_route.prose, "Caption route did not emit selected axis details")
_expect("standing oral position" in axis_route.prose, "Caption route lost item-axis position detail")
_expect("mouth contact at hip height" in axis_route.prose, "Caption route lost item-axis contact detail")
_expect("hands on hips" in axis_route.prose, "Caption route lost item-axis split detail")
group = {
"primary_subject": "group scene",
"subject_phrase": "three adult friends",
"age": "late 20s adults",
"item": "coordinated evening outfits",
"scene_text": "rooftop lounge with city lights",
"expression": "relaxed shared smiles",
"composition": "wide group frame",
}
_expect_caption_route_parity(
"caption_route_group",
group,
caption_metadata_routes.group_or_layout_from_row_result,
caption_naturalizer._group_or_layout_from_row,
"metadata(group_layout)",
)
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3511,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(hardcore_clothing_continuity="partially_removed"),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
)
_expect_pair(pair, "caption_route_pair")
_expect_caption_route_parity(
"caption_route_insta_pair",
pair,
caption_metadata_routes.insta_of_pair_from_row_result,
caption_naturalizer._insta_of_pair_from_row,
"metadata(insta_of_pair)",
)
deps = caption_naturalizer._caption_metadata_route_dependencies()
soft_route = caption_metadata_routes.insta_of_pair_from_row_result(
caption_naturalizer._caption_metadata_route_request(pair, "balanced", False, target="softcore"),
deps,
)
hard_route = caption_metadata_routes.insta_of_pair_from_row_result(
caption_naturalizer._caption_metadata_route_request(pair, "balanced", False, target="hardcore"),
deps,
)
_expect(soft_route is not None, "Caption pair softcore target did not match")
_expect(hard_route is not None, "Caption pair hardcore target did not match")
assert soft_route is not None
assert hard_route is not None
_expect("Softcore side:" not in soft_route.prose, "Caption softcore target should not keep combined pair labels")
_expect("Hardcore side:" not in soft_route.prose, "Caption softcore target should not include hard label")
_expect("Softcore side:" not in hard_route.prose, "Caption hardcore target should not include soft label")
_expect("Hardcore side:" not in hard_route.prose, "Caption hardcore target should not keep combined pair labels")
_expect(soft_route.prose != hard_route.prose, "Caption pair soft/hard targets should produce distinct prose")
shared_cast = pair.get("shared_cast_descriptors")
if isinstance(shared_cast, list):
shared_cast_text = "; ".join(str(item or "").strip() for item in shared_cast if str(item or "").strip())
else:
shared_cast_text = str(shared_cast or "").strip()
shared_cast_caption = caption_naturalizer._natural_cast_descriptor_text(shared_cast_text)
if shared_cast_caption:
_expect(
hard_route.prose.count(shared_cast_caption) <= 1,
"Caption hardcore target repeated shared cast descriptors",
)
public_hard, public_hard_method = caption_naturalizer.naturalize_caption(
"",
metadata_json=_json(pair),
input_hint="metadata_json",
target="hardcore",
trigger=Trigger,
include_trigger=False,
)
_expect(public_hard == hard_route.prose, "Public caption hardcore target drifted from typed route")
_expect("metadata(insta_of_pair)" in public_hard_method, "Public caption hardcore target lost pair method")
def smoke_sdxl_presets_policy() -> None:
_expect(
sdxl_formatter.SDXL_STYLE_PRESETS is sdxl_presets.SDXL_STYLE_PRESETS,
"SDXL formatter style presets should delegate to sdxl_presets",
)
_expect(
sdxl_formatter.SDXL_QUALITY_PRESETS is sdxl_presets.SDXL_QUALITY_PRESETS,
"SDXL formatter quality presets should delegate to sdxl_presets",
)
_expect(
sdxl_formatter.SDXL_FORMATTER_PROFILES is sdxl_presets.SDXL_FORMATTER_PROFILES,
"SDXL formatter profiles should delegate to sdxl_presets",
)
_expect(
sdxl_formatter.SDXL_ACTION_FAMILY_TAGS is sdxl_presets.SDXL_ACTION_FAMILY_TAGS,
"SDXL formatter action-family tags should delegate to sdxl_presets",
)
_expect("sdxl_photo" in sdxl_presets.sdxl_formatter_profile_choices(), "SDXL profile choices lost sdxl_photo")
_expect("flat_vector_pony" in sdxl_presets.sdxl_style_preset_choices(), "SDXL style preset choices lost default")
_expect("pony_high" in sdxl_presets.sdxl_quality_preset_choices(), "SDXL quality preset choices lost default")
_expect(
sdxl_presets.normalize_formatter_profile("bad") == sdxl_presets.DEFAULT_FORMATTER_PROFILE,
"SDXL invalid profile fallback changed",
)
_expect(sdxl_presets.normalize_formatter_profile("SDXL Photo") == "sdxl_photo", "SDXL profile should normalize spaces/case")
_expect(sdxl_presets.normalize_style_preset("bad") == sdxl_presets.DEFAULT_STYLE_PRESET, "SDXL invalid style fallback changed")
_expect(sdxl_presets.normalize_style_preset("flat-vector-pony") == "flat_vector_pony", "SDXL style should normalize hyphens")
_expect(sdxl_presets.normalize_quality_preset("bad") == sdxl_presets.DEFAULT_QUALITY_PRESET, "SDXL invalid quality fallback changed")
_expect(sdxl_presets.normalize_quality_preset("Pony High") == "pony_high", "SDXL quality should normalize spaces/case")
_expect(
sdxl_presets.apply_formatter_profile(
"sdxl_photo",
style_preset="flat_vector_pony",
quality_preset="pony_high",
)
== ("photographic", "sdxl_high"),
"SDXL photo profile overrides changed",
)
_expect(
sdxl_presets.apply_formatter_profile(
"manual_controls",
style_preset="flat_vector",
quality_preset="none",
)
== ("flat_vector", "none"),
"SDXL manual profile should preserve explicit controls",
)
row = _fixture_hardcore_row(
action_family="oral",
position_family="oral",
position_key="kneeling_oral",
position_keys=["kneeling_oral"],
)
tags = sdxl_formatter._metadata_family_tags(row)
_expect("oral sex" in tags, "SDXL metadata family tags lost oral family tag")
_expect("kneeling oral" in tags, "SDXL metadata family tags lost position key tag")
formatted = sdxl_formatter.format_sdxl_prompt(
_json(row),
input_hint="auto",
style_preset="bad",
quality_preset="bad",
trigger=SdxlTrigger,
prepend_trigger=True,
)
_expect_trigger_once("sdxl_presets.formatted_prompt", formatted.get("sdxl_prompt"), SdxlTrigger)
_expect("Flat vector" in formatted.get("sdxl_prompt", ""), "SDXL invalid style did not fall back to default preset")
_expect("score_9" in formatted.get("sdxl_prompt", ""), "SDXL invalid quality did not fall back to default preset")
profiled = sdxl_formatter.format_sdxl_prompt(
_json(row),
input_hint="auto",
formatter_profile="sdxl_photo",
style_preset="flat_vector_pony",
quality_preset="pony_high",
trigger=SdxlTrigger,
prepend_trigger=True,
)
profiled_prompt = profiled.get("sdxl_prompt", "")
_expect("realistic photo" in profiled_prompt, "SDXL photo profile did not apply photographic style")
_expect("score_9" not in profiled_prompt, "SDXL photo profile should switch away from Pony score quality tail")
def smoke_sdxl_format_route_policy() -> None:
row = _prompt_row(
name="sdxl_format_route_single",
category="woman",
subcategory="random",
seed=3701,
men_count=0,
camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
)
single_request = sdxl_format_route.SDXLFormatRequest(
source_text="",
metadata_json=_json(row),
target="single",
style_preset="flat_vector_pony",
quality_preset="pony_high",
trigger=SdxlTrigger,
prepend_trigger=True,
nude_weight=9.0,
extra_positive="sdxl route marker",
extra_negative="sdxl route negative",
formatter_profile="sdxl_photo",
)
typed_single = sdxl_format_route.format_sdxl_prompt_result(
single_request,
sdxl_formatter._sdxl_format_dependencies(),
)
public_single = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=single_request.metadata_json,
target=single_request.target,
style_preset=single_request.style_preset,
quality_preset=single_request.quality_preset,
trigger=single_request.trigger,
prepend_trigger=single_request.prepend_trigger,
nude_weight=single_request.nude_weight,
extra_positive=single_request.extra_positive,
extra_negative=single_request.extra_negative,
formatter_profile=single_request.formatter_profile,
)
_expect(typed_single.output == public_single, "Typed SDXL format route should match public single formatter output")
_expect(typed_single.branch == "metadata", "Typed SDXL format route changed single branch")
_expect(typed_single.target == "single", "Typed SDXL format route lost target normalization")
_expect(typed_single.nude_weight == 3.0, "Typed SDXL format route should clamp high nude weight")
_expect(typed_single.style_preset == "photographic", "Typed SDXL format route lost profile style override")
single_trace = json.loads(_expect_text("sdxl_format_route_policy.single_trace", typed_single.output.get("route_trace_json"), 20))
_expect(single_trace.get("formatter") == "sdxl", "Typed SDXL single trace lost formatter")
_expect(single_trace.get("branch") == "metadata", "Typed SDXL single trace lost branch")
_expect(single_trace.get("target") == "single", "Typed SDXL single trace lost target")
_expect(single_trace.get("nude_weight") == 3.0, "Typed SDXL single trace lost clamped nude weight")
_expect("sdxl route marker" in typed_single.output.get("sdxl_prompt", ""), "Typed SDXL route lost extra positive")
_expect("sdxl route negative" in typed_single.output.get("negative_prompt", ""), "Typed SDXL route lost extra negative")
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3702,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
)
pair_request = sdxl_format_route.SDXLFormatRequest(
source_text="",
metadata_json=_json(pair),
target="hardcore",
trigger=SdxlTrigger,
prepend_trigger=True,
extra_positive="pair sdxl route marker",
extra_negative="pair sdxl route negative",
)
typed_pair = sdxl_format_route.format_sdxl_prompt_result(
pair_request,
sdxl_formatter._sdxl_format_dependencies(),
)
public_pair = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=pair_request.metadata_json,
target=pair_request.target,
trigger=pair_request.trigger,
prepend_trigger=pair_request.prepend_trigger,
extra_positive=pair_request.extra_positive,
extra_negative=pair_request.extra_negative,
)
_expect(typed_pair.output == public_pair, "Typed SDXL format route should match public pair formatter output")
_expect(typed_pair.branch == "insta_of_pair", "Typed SDXL format route changed pair branch")
pair_trace = json.loads(_expect_text("sdxl_format_route_policy.pair_trace", typed_pair.output.get("route_trace_json"), 20))
_expect(pair_trace.get("branch") == "insta_of_pair", "Typed SDXL pair trace lost branch")
_expect(pair_trace.get("selected_side") == "hardcore", "Typed SDXL pair trace lost selected side")
_expect_text("sdxl_format_route_policy.hard_prompt", typed_pair.output.get("sdxl_hardcore_prompt"), 40)
_expect("pair sdxl route marker" in typed_pair.output.get("sdxl_prompt", ""), "Typed SDXL pair route lost extra positive")
fallback_request = sdxl_format_route.SDXLFormatRequest(
source_text="Characters: woman. Erotic outfit: sheer dress. Camera: side view. Avoid: blur",
input_hint="prompt",
target="weird",
trigger=SdxlTrigger,
prepend_trigger=False,
nude_weight=0.01,
style_preset="none",
quality_preset="none",
)
typed_fallback = sdxl_format_route.format_sdxl_prompt_result(
fallback_request,
sdxl_formatter._sdxl_format_dependencies(),
)
public_fallback = sdxl_formatter.format_sdxl_prompt(
fallback_request.source_text,
input_hint=fallback_request.input_hint,
target=fallback_request.target,
trigger=fallback_request.trigger,
prepend_trigger=fallback_request.prepend_trigger,
nude_weight=fallback_request.nude_weight,
style_preset=fallback_request.style_preset,
quality_preset=fallback_request.quality_preset,
)
_expect(typed_fallback.output == public_fallback, "Typed SDXL format route should match public fallback output")
_expect(typed_fallback.branch == "fallback", "Typed SDXL format route changed fallback branch")
_expect(typed_fallback.target == "auto", "Typed SDXL format route should normalize invalid target")
_expect(typed_fallback.nude_weight == 0.1, "Typed SDXL format route should clamp low nude weight")
fallback_trace = json.loads(_expect_text("sdxl_format_route_policy.fallback_trace", typed_fallback.output.get("route_trace_json"), 20))
_expect(fallback_trace.get("branch") == "fallback", "Typed SDXL fallback trace lost branch")
_expect(fallback_trace.get("input_hint") == "prompt", "Typed SDXL fallback trace lost input hint")
_expect("Characters:" not in typed_fallback.output.get("sdxl_prompt", ""), "Typed SDXL fallback leaked Characters label")
_expect("blur" in typed_fallback.output.get("negative_prompt", ""), "Typed SDXL fallback route lost Avoid negative")
def smoke_sdxl_tag_policy() -> None:
row = _fixture_hardcore_row(
action_family="oral",
position_family="oral",
position_key="kneeling_oral",
position_keys=["kneeling_oral"],
formatter_hints={"sdxl": ["policy route tag"]},
)
_expect(
sdxl_formatter._split_tag_text("Woman A with camera, Man A")
== sdxl_tag_policy.split_tag_text("Woman A with camera, Man A"),
"SDXL formatter split helper should delegate to sdxl_tag_policy",
)
_expect(
sdxl_formatter._metadata_family_tags(row) == sdxl_tag_policy.metadata_family_tags(row),
"SDXL formatter metadata-family helper should delegate to sdxl_tag_policy",
)
axis_row = {
"item_axis_values": {
"position": "edge-supported kneeling pose",
"contact_detail": "hands braced on thighs, close body alignment",
"ignored": "random",
}
}
axis_tags = sdxl_tag_policy.axis_value_tags(axis_row)
_expect("edge-supported kneeling pose" in axis_tags, "SDXL axis tags lost selected position axis")
_expect("hands braced on thighs" in axis_tags, "SDXL axis tags lost selected detail axis")
_expect("close body alignment" in axis_tags, "SDXL axis tags lost split detail axis")
_expect("random" not in axis_tags, "SDXL axis tags should ignore random placeholders")
hyphenated_tags = sdxl_tag_policy.split_tag_text("front-and-back penetration with hands on hips")
_expect("front-and-back penetration" in hyphenated_tags, "SDXL tag splitter broke hyphenated and compound")
_expect("front-" not in hyphenated_tags and "-back penetration" not in hyphenated_tags, "SDXL tag splitter emitted broken hyphen fragments")
_expect("hands on hips" in hyphenated_tags, "SDXL tag splitter stopped splitting non-hyphenated with connector")
subject_pair_tags = sdxl_tag_policy.split_tag_text("Woman A, Man A are mid-transition with hands on hips")
_expect("woman and man are mid-transition" in subject_pair_tags, "SDXL tag splitter broke paired character clause")
_expect("woman" not in subject_pair_tags and "man are mid-transition" not in subject_pair_tags, "SDXL tag splitter emitted broken paired character fragments")
sentence_boundary_tags = sdxl_tag_policy.split_tag_text("keep hands on hips, breasts, thighs. Man watches close")
sentence_boundary_tags_lower = [tag.lower() for tag in sentence_boundary_tags]
_expect("hands on hips" in sentence_boundary_tags_lower, "SDXL tag splitter did not clean leading keep imperative")
_expect("keep hands on hips" not in sentence_boundary_tags_lower, "SDXL tag splitter kept leading keep imperative")
_expect("thighs" in sentence_boundary_tags, "SDXL tag splitter lost pre-period tag")
_expect("man watches close" in sentence_boundary_tags_lower, "SDXL tag splitter did not split sentence-boundary tag")
_expect(
"thighs. man watches close" not in sentence_boundary_tags_lower,
"SDXL tag splitter kept sentence-boundary tag fragment",
)
_expect(
sdxl_formatter._camera_tags(row) == sdxl_tag_policy.camera_tags(row),
"SDXL formatter camera helper should delegate to sdxl_tag_policy",
)
_expect(
sdxl_formatter._combine_tags("a, b", "a", "c")
== sdxl_tag_policy.combine_tags("a, b", "a", "c")
== "a, b, c",
"SDXL tag combining changed",
)
deps = sdxl_formatter._sdxl_tag_route_dependencies()
_expect(deps.tag_key is sdxl_tag_policy.tag_key, "SDXL route deps lost policy tag_key")
_expect(deps.normal_character_tags is sdxl_tag_policy.normal_character_tags, "SDXL route deps lost character tag policy")
_expect(deps.metadata_family_tags is sdxl_tag_policy.metadata_family_tags, "SDXL route deps lost metadata family policy")
_expect(deps.axis_value_tags is sdxl_tag_policy.axis_value_tags, "SDXL route deps lost axis-value tag policy")
_expect(deps.camera_tags is sdxl_tag_policy.camera_tags, "SDXL route deps lost camera tag policy")
_expect(deps.explicit_tags is sdxl_tag_policy.explicit_tags, "SDXL route deps lost explicit tag policy")
_expect(
deps.filter_incompatible_route_tags is sdxl_tag_policy.filter_incompatible_route_tags,
"SDXL route deps lost route-family tag filter",
)
_expect(deps.softcore_pair_tags is sdxl_tag_policy.softcore_pair_tags, "SDXL route deps lost softcore pair tag policy")
mouth_nearby_tags = sdxl_tag_policy.explicit_tags(
"missionary penetration with mouth close to the ear",
1.29,
)
_expect("penetration" in mouth_nearby_tags, "SDXL explicit tags lost penetration signal")
_expect("oral sex" not in mouth_nearby_tags, "SDXL explicit tags should not treat nearby mouth wording as oral")
outercourse_filtered_tags = sdxl_tag_policy.filter_incompatible_route_tags(
["outercourse", "penis licking", "oral sex", "penetration"],
_fixture_hardcore_row(
action_family="outercourse",
position_family="outercourse",
position_key="penis_licking",
position_keys=["penis_licking"],
),
)
_expect("outercourse" in outercourse_filtered_tags, "SDXL route filter removed matching outercourse tag")
_expect("penis licking" in outercourse_filtered_tags, "SDXL route filter removed specific outercourse key")
_expect("oral sex" not in outercourse_filtered_tags, "SDXL route filter kept incompatible oral tag")
_expect("penetration" not in outercourse_filtered_tags, "SDXL route filter kept incompatible penetration tag")
stale_character_row = {
"prompt": "Characters: 99-year-old adult man, stale body, stale skin, stale hair, stale eyes.",
"age_band": "27-year-old adult",
"subject_phrase": "woman",
"body_phrase": "athletic figure",
"skin": "warm olive skin",
"hair": "short black hair",
"eyes": "green eyes",
}
stale_character_tags = sdxl_tag_policy.normal_character_tags(stale_character_row)
_expect("27-year-old adult" in stale_character_tags, "SDXL character tags lost structured age")
_expect("warm olive skin" in stale_character_tags, "SDXL character tags lost structured skin")
_expect(
all("stale" not in tag for tag in stale_character_tags),
"SDXL character tags should not parse stale raw prompt character labels",
)
descriptor_tags = sdxl_tag_policy.normal_character_tags(
{
"prompt": "Characters: stale prompt descriptor.",
"cast_descriptor_text": "Woman A: 30-year-old adult woman, toned figure, fair skin, red hair, gray eyes",
}
)
_expect("30-year-old adult woman" in descriptor_tags, "SDXL character tags lost cast descriptor metadata")
_expect("toned build" in descriptor_tags, "SDXL character tags did not normalize descriptor figure tag")
_expect(all("stale" not in tag for tag in descriptor_tags), "SDXL cast descriptor metadata should beat stale prompt labels")
def smoke_sdxl_tag_routes() -> None:
row = _fixture_hardcore_row(
formatter_hints={
"all": ["shared route anchor"],
"sdxl": ["sdxl route tag"],
}
)
deps = sdxl_formatter._sdxl_tag_route_dependencies()
typed_row = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(row, 1.29),
deps,
)
_expect(
typed_row.tags == sdxl_formatter._row_core_tags(row, 1.29),
"Typed SDXL row tag route should match legacy wrapper output",
)
_expect("sdxl route tag" in typed_row.as_text(), "Typed SDXL row tag route lost route-specific formatter hint")
axis_only_row = _fixture_hardcore_row(
item="generic configured adult action",
pose="configured explicit pose",
role_graph="",
source_role_graph="",
item_axis_values={
"position": "standing oral position",
"contact_detail": "mouth contact at hip height, hands on hips",
},
action_family="oral",
position_family="oral",
position_key="standing",
position_keys=["standing"],
)
axis_only_tags = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(axis_only_row, 1.29),
deps,
).as_text()
_expect("standing oral position" in axis_only_tags, "SDXL row route lost item axis position tag")
_expect("mouth contact at hip height" in axis_only_tags, "SDXL row route lost item axis contact tag")
_expect("hands on hips" in axis_only_tags, "SDXL row route lost split item axis detail tag")
stale_character_route_row = _fixture_hardcore_row(
prompt="Characters: stale prompt subject, stale body, stale skin, stale hair, stale eyes.",
cast_descriptor_text="",
cast_descriptors=[],
subject_type="woman",
subject_phrase="woman",
primary_subject="woman",
age_band="27-year-old adult",
body_phrase="athletic figure",
skin="warm olive skin",
hair="short black hair",
eyes="green eyes",
women_count=1,
men_count=0,
person_count=1,
)
stale_character_route_tags = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(stale_character_route_row, 1.29),
deps,
).as_text()
_expect("27-year-old adult" in stale_character_route_tags, "SDXL route lost structured character age")
_expect("warm olive skin" in stale_character_route_tags, "SDXL route lost structured character skin")
_expect("stale" not in stale_character_route_tags, "SDXL route should not parse stale prompt character labels")
composition_label_row = _fixture_hardcore_row(
composition="coworking lounge frame with tall-window depth behind them composition",
)
composition_label_tags = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(composition_label_row, 1.29),
deps,
).as_text()
_expect("tall-window depth behind them composition" not in composition_label_tags, "SDXL route kept raw composition label tag")
_expect("tall-window depth behind them" in composition_label_tags, "SDXL route lost composition detail while removing label")
expression_label_row = _fixture_hardcore_row(
character_expression_text="Woman A has focused gaze; Man A has steady expression",
expression="stale expression",
)
expression_label_tags = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(expression_label_row, 1.29),
deps,
).as_text()
_expect("woman has" not in expression_label_tags.lower(), "SDXL route kept woman-has expression label")
_expect("man has" not in expression_label_tags.lower(), "SDXL route kept man-has expression label")
_expect("focused gaze" in expression_label_tags, "SDXL route lost cleaned woman expression")
_expect("steady expression" in expression_label_tags, "SDXL route lost cleaned man expression")
stale_prompt_row = _fixture_hardcore_row(
prompt="stale raw prompt mentions fully nude naked pussy penis oral anal semen penetration",
item="standing portrait setup",
pose="standing pose",
role_graph="",
source_role_graph="",
expression="neutral expression",
action_family="",
position_family="",
position_key="",
position_keys=[],
)
stale_tags = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(stale_prompt_row, 1.29),
deps,
).as_text()
for forbidden in ("(naked:", "pussy", "penis", "oral sex", "anal sex", "semen", "penetration"):
_expect(forbidden not in stale_tags, f"SDXL row tags should not infer explicit tag from stale prompt: {forbidden}")
metadata_explicit_row = _fixture_hardcore_row(
prompt="stale raw prompt without explicit tag anchors",
item="kneeling pose with visible penis and pussy contact",
pose="penetration pose",
role_graph="penis thrusts into pussy",
source_role_graph="penis thrusts into pussy",
hardcore_clothing_state="fully nude body, bare skin unobstructed",
action_family="penetration",
position_family="penetrative",
position_key="kneeling",
position_keys=["kneeling"],
)
metadata_tags = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(metadata_explicit_row, 1.29),
deps,
).as_text()
for required in ("(naked:1.29)", "pussy", "penis", "penetration"):
_expect(required in metadata_tags, f"SDXL row tags lost structured explicit metadata tag: {required}")
outercourse_noise_row = _fixture_hardcore_row(
item="penis-licking outercourse position with tongue along the penis shaft",
pose="configured outercourse pose",
role_graph="Woman A bends low while her tongue runs along Man A's penis shaft.",
source_role_graph="Woman A bends low while her tongue runs along Man A's penis shaft.",
item_axis_values={
"position": "penis-licking outercourse position",
"outer_act": "tongue along the penis shaft",
},
action_family="outercourse",
position_family="outercourse",
position_key="penis_licking",
position_keys=["penis_licking"],
)
outercourse_noise_tags = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(outercourse_noise_row, 1.29),
deps,
).as_text()
_expect("outercourse" in outercourse_noise_tags, "SDXL outercourse row lost matching family tag")
_expect("penis licking" in outercourse_noise_tags, "SDXL outercourse row lost specific position key")
_expect("oral sex" not in outercourse_noise_tags, "SDXL outercourse row kept incompatible oral tag")
_expect("penetration" not in outercourse_noise_tags, "SDXL outercourse row kept incompatible penetration tag")
stale_hardcore_pose_row = _fixture_hardcore_row(
item="oral contact with mouth on the visible genitals in side-lying oral position",
pose="kneeling and balancing a cucumber upright on an open palm held overhead",
role_graph="Woman A lies on her side while Man A's mouth is pressed to her pussy.",
source_role_graph="Woman A lies on her side while Man A's mouth is pressed to her pussy.",
item_axis_values={
"position": "side-lying oral position",
"oral_act": "oral contact with mouth on the visible genitals",
},
action_family="oral",
position_family="oral",
position_key="side_lying",
position_keys=["side_lying"],
)
stale_hardcore_pose_tags = sdxl_tag_routes.row_core_tags_result(
sdxl_tag_routes.SDXLRowTagRequest(stale_hardcore_pose_row, 1.29),
deps,
).as_text()
_expect("oral sex" in stale_hardcore_pose_tags, "SDXL hardcore route lost oral family tag")
_expect("side lying" in stale_hardcore_pose_tags, "SDXL hardcore route lost structured position key")
_expect("cucumber" not in stale_hardcore_pose_tags, "SDXL hardcore route leaked generic stale pose text")
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3511,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(hardcore_clothing_continuity="partially_removed"),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
)
_expect_pair(pair, "sdxl_tag_routes_pair")
soft_row = pair.get("softcore_row") if isinstance(pair.get("softcore_row"), dict) else {}
hard_row = pair.get("hardcore_row") if isinstance(pair.get("hardcore_row"), dict) else {}
typed_soft = sdxl_tag_routes.soft_tags_result(
sdxl_tag_routes.SDXLPairTagRequest(soft_row, pair, 1.29),
deps,
)
typed_hard = sdxl_tag_routes.hard_tags_result(
sdxl_tag_routes.SDXLPairTagRequest(hard_row, pair, 1.29),
deps,
)
_expect(
typed_soft.as_text() == sdxl_formatter._soft_tags(soft_row, pair, 1.29),
"Typed SDXL pair soft tag route should match legacy wrapper output",
)
_expect(
typed_hard.as_text() == sdxl_formatter._hard_tags(hard_row, pair, 1.29),
"Typed SDXL pair hard tag route should match legacy wrapper output",
)
pair_stale_hard_row = _fixture_hardcore_row(
prompt="",
item="standing portrait setup",
pose="standing pose",
role_graph="",
source_role_graph="",
expression="neutral expression",
composition="standing portrait frame",
action_family="",
position_family="",
position_key="",
position_keys=[],
)
pair_stale_root = {
"hardcore_prompt": "stale pair prompt says fully nude naked pussy penis oral anal semen penetration",
"hardcore_women_count": 1,
"hardcore_men_count": 1,
}
pair_stale_tags = sdxl_tag_routes.hard_tags_result(
sdxl_tag_routes.SDXLPairTagRequest(pair_stale_hard_row, pair_stale_root, 1.29),
deps,
).as_text()
for forbidden in ("(naked:", "pussy", "penis", "oral sex", "anal sex", "semen", "penetration"):
_expect(forbidden not in pair_stale_tags, f"SDXL pair tags should not infer explicit tag from stale prompt: {forbidden}")
pair_metadata_root = {
"hardcore_prompt": "stale pair prompt without explicit tag anchors",
"hardcore_women_count": 1,
"hardcore_men_count": 1,
"hardcore_clothing_state": "fully nude body, bare skin unobstructed",
}
pair_metadata_hard_row = _fixture_hardcore_row(
prompt="",
item="kneeling pose with visible penis and pussy contact",
role_graph="penis thrusts into pussy",
source_role_graph="penis thrusts into pussy",
action_family="penetration",
position_family="penetrative",
position_key="kneeling",
position_keys=["kneeling"],
)
pair_metadata_tags = sdxl_tag_routes.hard_tags_result(
sdxl_tag_routes.SDXLPairTagRequest(pair_metadata_hard_row, pair_metadata_root, 1.29),
deps,
).as_text()
for required in ("(naked:1.29)", "pussy", "penis", "penetration"):
_expect(required in pair_metadata_tags, f"SDXL pair tags lost structured explicit metadata tag: {required}")
pair_axis_tags = sdxl_tag_routes.hard_tags_result(
sdxl_tag_routes.SDXLPairTagRequest(axis_only_row, pair_metadata_root, 1.29),
deps,
).as_text()
_expect("standing oral position" in pair_axis_tags, "SDXL pair hard route lost item axis position tag")
_expect("mouth contact at hip height" in pair_axis_tags, "SDXL pair hard route lost item axis contact tag")
formatted = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=_json(pair),
target="hardcore",
trigger=SdxlTrigger,
prepend_trigger=True,
)
_expect("sdxl(insta_of_pair)" in formatted.get("method", ""), "SDXL pair formatter route changed method")
def smoke_hardcore_position_config_policy() -> None:
_expect(
pb.HARDCORE_POSITION_FAMILY_CHOICES is hardcore_position_config.HARDCORE_POSITION_FAMILY_CHOICES,
"Prompt builder hardcore position family choices are not delegated",
)
_expect("outercourse_only" in hardcore_position_config.hardcore_position_focus_choices(), "Hardcore focus choices lost outercourse_only")
_expect("boobjob" in hardcore_position_config.hardcore_position_key_choices(), "Hardcore position keys lost boobjob")
_expect(
category_template_metadata.template_action_family({"action_family": "toy double"}) == "toy_double",
"Template action-family normalizer should accept spaced aliases",
)
_expect(
category_template_metadata.template_action_family({"action_family": "manual stimulation"}) == "manual",
"Template action-family normalizer should accept subcategory-style aliases",
)
_expect(
category_template_metadata.template_action_family({"action_family": "anal sex"}) == "anal",
"Template action-family normalizer should accept anal aliases",
)
_expect(
category_template_metadata.template_action_family({"action_family": "three way"}) == "threesome",
"Template action-family normalizer should accept threesome aliases",
)
_expect(
category_template_metadata.template_action_family({"action_family": "group sex"}) == "group",
"Template action-family normalizer should accept group aliases",
)
_expect(
category_template_metadata.template_position_family({"position_family": "penetration"}) == "penetrative",
"Template position-family normalizer should accept action-style aliases",
)
_expect(
category_template_metadata.template_position_family({"position_family": "outer-course"}) == "outercourse",
"Template position-family normalizer should accept hyphenated aliases",
)
base = json.loads(
pb.build_hardcore_position_pool_json(
combine_mode="replace",
family="oral",
selected_positions=["standing", "bad value", "standing"],
)
)
_expect(base.get("enabled") is True, "Hardcore position pool should enable config")
_expect(base.get("family") == "oral", "Hardcore position pool lost family")
_expect(base.get("positions") == ["standing"], "Hardcore position normalization changed")
_expect(base.get("require_position") is True, "Hardcore position pool should require selected position")
added = json.loads(
hardcore_position_config.build_hardcore_position_pool_json(
hardcore_position_config=base,
combine_mode="add",
family="any",
selected_positions=["kneeling", "standing"],
)
)
_expect(added.get("positions") == ["standing", "kneeling"], "Hardcore position add merge changed")
filtered = json.loads(
pb.build_hardcore_action_filter_json(
hardcore_position_config=added,
focus="outercourse_only",
allow_toys=False,
allow_double=False,
allow_penetration=True,
allow_foreplay=True,
allow_interaction=True,
allow_manual=True,
allow_oral=True,
allow_outercourse=True,
allow_anal=True,
allow_climax=True,
)
)
_expect(filtered.get("family") == "outercourse", "Hardcore action focus did not set outercourse family")
_expect(filtered.get("allow_oral") is False, "Hardcore outercourse focus should disable oral")
_expect(filtered.get("allow_penetration") is False, "Hardcore outercourse focus should disable penetration")
_expect("outercourse_sex" in hardcore_position_config.hardcore_allowed_subcategory_slugs(filtered), "Allowed subcategories lost outercourse")
_expect("oral_sex" not in hardcore_position_config.hardcore_allowed_subcategory_slugs(filtered), "Allowed subcategories should exclude oral")
strict_threesome = json.loads(
pb.build_hardcore_action_filter_json(
hardcore_position_config=pb.build_hardcore_position_pool_json(family="threesome"),
focus="threesome_only",
allow_toys=False,
allow_double=False,
allow_penetration=False,
allow_foreplay=False,
allow_interaction=False,
allow_manual=False,
allow_oral=False,
allow_outercourse=False,
allow_anal=False,
allow_climax=False,
)
)
_expect(
hardcore_position_config.hardcore_allowed_subcategory_slugs(strict_threesome) == {"threesomes"},
"Specific hardcore family filter should not widen to the full pool when boolean filters empty it",
)
action_only = json.loads(
hardcore_position_config.build_hardcore_action_filter_json(
focus="outercourse_only",
allow_toys=False,
allow_double=False,
allow_penetration=True,
allow_foreplay=True,
allow_interaction=True,
allow_manual=True,
allow_oral=True,
allow_outercourse=True,
allow_anal=True,
allow_climax=True,
)
)
action_axis = hardcore_position_config.filter_hardcore_axis(
"outer_act",
["boobjob body contact", "blowjob oral sex", "vaginal penetration"],
action_only,
)
_expect(action_axis == ["boobjob body contact"], "Hardcore action filter policy did not block disabled oral/penetration text")
action_axis_metadata = hardcore_position_config.filter_hardcore_axis(
"outer_act",
[
{"text": "generic contact route", "action_family": "outercourse", "position_family": "outercourse"},
{"text": "generic contact route", "action_family": "oral", "position_family": "oral"},
{"text": "generic contact route", "action_family": "penetration", "position_family": "penetrative"},
],
action_only,
)
_expect(
action_axis_metadata == [{"text": "generic contact route", "action_family": "outercourse", "position_family": "outercourse"}],
"Hardcore action filter policy did not honor structured action metadata",
)
position_filtered = hardcore_position_config.apply_hardcore_position_config_to_subcategory(
{
"slug": "oral_sex",
"item_templates": [
{"template": "oral contact in {position}"},
{"template": "metadata-specific oral contact", "position_key": "standing", "action_family": "oral"},
{"template": "oral sex without a position axis"},
{"template": "unsupported static template"},
],
"item_axes": {
"position": [
"standing oral position",
"kneeling oral position",
{"text": "generic standing pose", "position_key": "standing"},
{"text": "generic kneeling pose", "position_key": "kneeling"},
],
"oral_act": ["blowjob", "cunnilingus"],
},
},
base,
)
_expect(
position_filtered["item_templates"]
== [
{"template": "oral contact in {position}"},
{"template": "metadata-specific oral contact", "position_key": "standing", "action_family": "oral"},
],
"Hardcore position policy did not filter templates by selected position requirements or metadata",
)
_expect(
position_filtered["item_axes"]["position"] == ["standing oral position", {"text": "generic standing pose", "position_key": "standing"}],
"Hardcore position policy did not filter position axes by selected keys or metadata",
)
filtered_categories = hardcore_position_config.filter_hardcore_categories_for_position(
[
{
"name": "Hardcore sexual poses",
"slug": "hardcore_sexual_poses",
"subcategories": [{"slug": "oral_sex"}, {"slug": "outercourse_sex"}],
},
{"name": "Casual clothes", "slug": "casual_clothes", "subcategories": [{"slug": "tops"}]},
],
filtered,
1,
1,
lambda _entry, _women, _men: True,
)
_expect(
[entry["slug"] for entry in filtered_categories[0]["subcategories"]] == ["outercourse_sex"],
"Hardcore category filter policy did not remove disallowed subcategories",
)
_expect(filtered_categories[1]["slug"] == "casual_clothes", "Hardcore category filter should preserve non-hardcore categories")
keys = pb._hardcore_position_keys("woman on all fours from behind", axis_values={"position": "doggy"})
_expect(keys == ["doggy"], "Hardcore position key detection changed")
source_family = hardcore_position_config.hardcore_source_position_family({"slug": "manual_stimulation"}, filtered)
_expect(source_family == "manual", "Hardcore source family lookup changed")
source_action_family = hardcore_action_metadata.source_hardcore_action_family(
"outer-course",
"",
"generic contact",
)
_expect(source_action_family == "outercourse", "Source action-family fallback should accept hyphenated source aliases")
_expect(
hardcore_action_metadata.source_hardcore_action_family("threesome", "", "three-body contact") == "threesome",
"Source action-family fallback should accept threesome source family",
)
_expect(
hardcore_action_metadata.source_hardcore_action_family("group", "", "group sex contact") == "group",
"Source action-family fallback should accept group source family",
)
default_action_route = row_route_metadata.resolve_action_position_route(
is_pose_category=True,
subcategory={"slug": "anal_double_penetration"},
hardcore_position_config=None,
item_template_metadata={"action_family": "default", "position_family": "anal"},
item_text="toy-assisted double penetration with front-and-back contact",
source_role_graph="one partner between two bodies",
source_composition="",
pose="",
item_axis_values={"double_act": "toy-assisted double penetration"},
)
_expect(default_action_route.get("position_family") == "anal", "Default-action metadata should preserve position family")
_expect(default_action_route.get("action_family") == "toy_double", "Default-action metadata should still allow action inference")
item_text, item_name, axis_values, template_metadata = pb._compose_item(
random.Random(42),
{},
{
"name": "Template metadata route",
"item_templates": [
{
"template": "{act} in {position}",
"action_family": "oral",
"position_family": "oral",
"position_keys": ["kneeling", "open_thighs"],
"formatter_hint": {
"krea2": "keep mouth contact readable",
"sdxl": ["oral contact", "kneeling oral"],
"training_caption": "oral contact caption detail",
},
}
],
"item_axes": {
"act": ["mouth contact"],
"position": ["kneeling oral position"],
},
},
"Template metadata route",
women_count=1,
men_count=1,
)
_expect(item_text == "mouth contact in kneeling oral position", "Template metadata route changed composed item text")
_expect(item_name == "Template metadata route", "Template metadata route changed item name")
_expect(axis_values == {"act": "mouth contact", "position": "kneeling oral position"}, "Template metadata route lost axis values")
_expect(template_metadata.get("action_family") == "oral", "Template metadata route lost action family")
_expect(pb._template_position_family(template_metadata) == "oral", "Template metadata route lost position family")
_expect(pb._template_position_keys(template_metadata) == ["kneeling", "open_thighs"], "Template metadata route lost position keys")
_expect(pb._template_action_family(template_metadata) == "oral", "Template metadata route lost normalized action family")
formatter_hints = pb._template_formatter_hints(template_metadata)
_expect(formatter_hints.get("krea") == ["keep mouth contact readable"], "Template metadata route lost Krea formatter hint")
_expect(formatter_hints.get("sdxl") == ["oral contact", "kneeling oral"], "Template metadata route lost SDXL formatter hints")
_expect(formatter_hints.get("caption") == ["oral contact caption detail"], "Template metadata route lost caption formatter hint")
inherited_text, _inherited_name, inherited_axis_values, inherited_metadata = pb._compose_item(
random.Random(42),
{},
{
"name": "Inherited metadata route",
"item_template_metadata": {
"action_family": "manual",
"position_family": "manual",
"position_keys": ["kneeling"],
"formatter_hint": {"caption": "inherited caption cue"},
},
"item_templates": ["{act} in {position}"],
"item_axes": {
"act": ["hand stimulation"],
"position": ["kneeling manual position"],
},
},
"Inherited metadata route",
women_count=1,
men_count=1,
)
_expect(inherited_text == "hand stimulation in kneeling manual position", "Inherited template metadata changed item text")
_expect(inherited_axis_values == {"act": "hand stimulation", "position": "kneeling manual position"}, "Inherited template metadata lost axis values")
_expect(inherited_metadata.get("action_family") == "manual", "String template did not inherit action family")
_expect(inherited_metadata.get("position_family") == "manual", "String template did not inherit position family")
_expect(pb._template_position_keys(inherited_metadata) == ["kneeling"], "String template did not inherit position keys")
_expect(
route_metadata.row_formatter_hints({"item_template_metadata": inherited_metadata}, "caption") == ["inherited caption cue"],
"String template did not inherit formatter hints",
)
override_text, _override_name, _override_axis_values, override_metadata = pb._compose_item(
random.Random(42),
{},
{
"name": "Override metadata route",
"item_template_metadata": {
"action_family": "manual",
"position_family": "manual",
"formatter_hint": {"all": "inherited shared cue"},
},
"item_templates": [
{
"template": "{act} in {position}",
"action_family": "oral",
"formatter_hint": {"krea2": "override krea cue"},
}
],
"item_axes": {
"act": ["mouth contact"],
"position": ["kneeling oral position"],
},
},
"Override metadata route",
women_count=1,
men_count=1,
)
_expect(override_text == "mouth contact in kneeling oral position", "Override template metadata changed item text")
_expect(override_metadata.get("action_family") == "oral", "Template object did not override inherited action family")
_expect(override_metadata.get("position_family") == "manual", "Template object should keep inherited position family when absent")
_expect(
route_metadata.row_formatter_hints({"item_template_metadata": override_metadata}, "krea")
== ["inherited shared cue", "override krea cue"],
"Template metadata did not merge inherited and template formatter hints",
)
route_row = {
"action_family": "penetrative",
"position_family": "Oral",
"position_keys": ["spread leg oral", "bad key"],
"position_key": "open thighs",
"formatter_hints": {"all": ["shared formatter cue"], "training_caption": ["caption formatter cue"]},
}
_expect(route_metadata.row_action_family(route_row) == "penetration", "Route metadata action normalization changed")
_expect(route_metadata.row_position_family(route_row) == "oral", "Route metadata position-family normalization changed")
_expect(
route_metadata.row_position_keys(route_row) == ["spread_leg_oral", "open_thighs"],
"Route metadata position-key normalization changed",
)
_expect(
route_metadata.row_position_keys({"position_keys": ["kneeling_oral"]}, include_unknown=True) == ["kneeling_oral"],
"Route metadata legacy position-key passthrough changed",
)
_expect(
route_metadata.row_formatter_hints(route_row, "caption") == ["shared formatter cue", "caption formatter cue"],
"Route metadata formatter hint routing changed",
)
nested_route_row = {
"item_template_metadata": {
"action_family": "oral",
"position_family": "oral",
"position_keys": ["kneeling", "open_thighs"],
"formatter_hint": {"krea2": "nested krea cue", "sdxl": "nested sdxl cue", "training_caption": "nested caption cue"},
}
}
_expect(
route_metadata.row_action_family(nested_route_row) == "oral",
"Route metadata should fall back to nested template action family",
)
_expect(
route_metadata.row_position_family(nested_route_row) == "oral",
"Route metadata should fall back to nested template position family",
)
_expect(
route_metadata.row_position_keys(nested_route_row) == ["kneeling", "open_thighs"],
"Route metadata should fall back to nested template position keys",
)
_expect(
route_metadata.row_formatter_hints(nested_route_row, "krea") == ["nested krea cue"],
"Route metadata should fall back to nested Krea formatter hints",
)
merged_route_row = {
"position_key": "standing",
"formatter_hints": {"all": ["shared cue"]},
"item_template_metadata": {
"position_keys": ["kneeling"],
"formatter_hint": {"caption": "nested caption cue"},
},
}
_expect(
route_metadata.row_position_keys(merged_route_row) == ["standing", "kneeling"],
"Route metadata should merge top-level and nested position keys",
)
_expect(
route_metadata.row_formatter_hints(merged_route_row, "caption") == ["shared cue", "nested caption cue"],
"Route metadata should merge top-level and nested formatter hints",
)
route_hints = category_template_metadata.formatter_hints_for_route(
{"formatter_hints": {"all": ["shared formatter cue"], "krea2": ["krea formatter cue"]}},
"krea2",
)
_expect(route_hints == ["shared formatter cue", "krea formatter cue"], "Formatter hint route resolver changed")
raw_route_hints = category_template_metadata.formatter_hints_for_route(
{"all": ["raw shared cue"], "caption": ["raw caption cue"]},
"caption",
)
_expect(raw_route_hints == ["raw shared cue", "raw caption cue"], "Raw formatter route-map resolver changed")
_expect(
category_template_metadata.formatter_hints_for_route({"caption": "row caption should not be a hint", "prompt": "row prompt"}, "caption")
== [],
"Formatter hint route resolver treated an arbitrary metadata row as route hints",
)
_expect(
pb._template_action_family(template_metadata) == category_template_metadata.template_action_family(template_metadata),
"Prompt builder template action policy should delegate",
)
_expect(
category_template_metadata.template_metadata_errors(template_metadata) == [],
"Valid template metadata should not report audit errors",
)
invalid_metadata = {
"action_family": "bad_action",
"position_family": "bad_family",
"position_keys": ["kneeling", "bad_position"],
"formatter_hint": {"bad_route": 9, "sdxl": ["ok", ""]},
}
invalid_errors = category_template_metadata.template_metadata_errors(invalid_metadata)
_expect(any("bad_action" in error for error in invalid_errors), "Template metadata validation missed bad action")
_expect(any("bad_family" in error for error in invalid_errors), "Template metadata validation missed bad family")
_expect(any("bad_position" in error for error in invalid_errors), "Template metadata validation missed bad position key")
_expect(any("bad_route" in error for error in invalid_errors), "Template metadata validation missed bad formatter route")
_expect(any("invalid formatter_hint" in error for error in invalid_errors), "Template metadata validation missed bad formatter hint value")
def smoke_row_route_metadata_policy() -> None:
template_metadata = {
"action_family": "oral",
"position_family": "oral",
"position_keys": ["kneeling", "open_thighs"],
}
route = row_route_metadata.resolve_action_position_route(
is_pose_category=True,
subcategory={"slug": "oral_sex"},
hardcore_position_config={},
item_template_metadata=template_metadata,
item_text="mouth contact in kneeling oral position",
source_role_graph="the woman kneels in front of the man",
source_composition="close kneeling oral composition",
pose="kneeling pose",
item_axis_values={"position": "kneeling oral position"},
)
_expect(route["action_family"] == "oral", "Route policy lost template action family")
_expect(route["position_family"] == "oral", "Route policy lost template position family")
_expect(route["position_key"] == "kneeling", "Route policy did not preserve first template position key")
_expect(route["position_keys"] == ["kneeling", "open_thighs"], "Route policy changed template position-key precedence")
route_result = row_route_metadata.resolve_action_position_route_result(
is_pose_category=True,
subcategory={"slug": "oral_sex"},
hardcore_position_config={},
item_template_metadata=template_metadata,
item_text="mouth contact in kneeling oral position",
source_role_graph="the woman kneels in front of the man",
source_composition="close kneeling oral composition",
pose="kneeling pose",
item_axis_values={"position": "kneeling oral position"},
)
_expect(route_result.as_dict() == route, "Typed action/position route should match legacy dict route")
_expect(route_result.position_key == "kneeling", "Typed action/position route lost first position key")
delegated = pb._action_position_route_metadata(
is_pose_category=True,
subcategory={"slug": "oral_sex"},
hardcore_position_config={},
item_template_metadata=template_metadata,
item_text="mouth contact in kneeling oral position",
source_role_graph="the woman kneels in front of the man",
source_composition="close kneeling oral composition",
pose="kneeling pose",
item_axis_values={"position": "kneeling oral position"},
)
_expect(delegated == route, "Prompt builder route wrapper should delegate to row_route_metadata")
typed_delegated = pb._action_position_route(
is_pose_category=True,
subcategory={"slug": "oral_sex"},
hardcore_position_config={},
item_template_metadata=template_metadata,
item_text="mouth contact in kneeling oral position",
source_role_graph="the woman kneels in front of the man",
source_composition="close kneeling oral composition",
pose="kneeling pose",
item_axis_values={"position": "kneeling oral position"},
)
_expect(typed_delegated == route_result, "Prompt builder typed route wrapper should delegate to row_route_metadata")
fallback = row_route_metadata.resolve_action_position_route(
is_pose_category=True,
subcategory={"slug": "manual_stimulation"},
hardcore_position_config={},
item_template_metadata={},
item_text="manual stimulation while kneeling",
source_role_graph="the woman kneels close and uses her hand",
source_composition="kneeling manual composition",
pose="kneeling pose",
item_axis_values={"position": "kneeling manual position"},
)
_expect(fallback["position_family"] == "manual", "Route policy lost source position-family fallback")
_expect(fallback["action_family"] == "manual", "Route policy lost source action-family fallback")
_expect("kneeling" in fallback["position_keys"], "Route policy lost inferred position key")
empty = row_route_metadata.resolve_action_position_route(
is_pose_category=False,
subcategory={"slug": "casual_clothes"},
hardcore_position_config={},
item_template_metadata=template_metadata,
item_text="casual outfit",
source_role_graph="",
source_composition="",
pose="standing pose",
)
_expect(empty == row_route_metadata.empty_action_position_route(), "Non-pose route should return empty route metadata")
_expect(
row_route_metadata.empty_action_position_route_result().as_dict() == empty,
"Typed empty action/position route should match legacy dict route",
)
def smoke_category_library_route() -> None:
categories = category_library.load_category_library()
_expect(len(categories) >= 3, "category library should load JSON categories")
category, subcategory, women_count, men_count = category_library.find_subcategory(
categories,
"custom_random",
"Hardcore sexual poses / Oral sex",
random.Random(101),
random.Random(102),
women_count=1,
men_count=1,
)
_expect(category.get("slug") == "hardcore_sexual_poses", "exact category lookup selected wrong category")
_expect(subcategory.get("slug") == "oral_sex", "exact subcategory lookup selected wrong subcategory")
_expect((women_count, men_count) == (1, 1), "exact subcategory lookup changed compatible cast counts")
slash_categories = [
{"name": "Dev", "slug": "dev", "subcategories": [{"name": "Wrong Route", "slug": "wrong_route", "items": ["wrong item"]}]},
{
"name": "Dev / Test Wear",
"slug": "dev_test_wear",
"subcategories": [{"name": "Layered / Office", "slug": "layered_office", "items": ["structured test item"]}],
},
]
slash_selector = category_library.exact_subcategory_selector(
slash_categories[1],
slash_categories[1]["subcategories"][0],
)
slash_choice = category_library.split_exact_subcategory_choice(slash_categories, slash_selector)
_expect(slash_choice is not None, "Exact selector parser did not accept slash-bearing category/subcategory names")
if slash_choice is not None:
_expect(slash_choice[0].get("slug") == "dev_test_wear", "Exact selector parser did not prefer longest category prefix")
_expect(slash_choice[1] == "Layered / Office", "Exact selector parser trimmed slash-bearing subcategory incorrectly")
slash_category, slash_subcategory, _slash_women, _slash_men = category_library.find_subcategory(
slash_categories,
"custom_random",
slash_selector,
random.Random(201),
random.Random(202),
women_count=1,
men_count=0,
)
_expect(slash_category.get("slug") == "dev_test_wear", "Exact subcategory lookup failed slash-bearing category")
_expect(slash_subcategory.get("slug") == "layered_office", "Exact subcategory lookup failed slash-bearing subcategory")
item = category_library.compatible_entries(list(subcategory.get("items") or []), women_count, men_count)[0]
scenes = category_library.configured_pool(
category,
subcategory,
item,
"scenes",
"scene_pools",
category_library.load_scene_pool_library(),
"inherit_scenes",
)
expressions = category_library.configured_pool(
category,
subcategory,
item,
"expressions",
"expression_pools",
category_library.load_expression_pool_library(),
"inherit_expressions",
)
compositions = category_library.configured_pool(
category,
subcategory,
item,
"compositions",
"composition_pools",
category_library.load_composition_pool_library(),
"inherit_compositions",
)
_expect(scenes, "category inheritance did not resolve scenes")
_expect(expressions, "category inheritance did not resolve expressions")
_expect(compositions, "category inheritance did not resolve compositions")
_expect(any("oral" in _clean_key(entry.get("prompt") if isinstance(entry, dict) else entry) for entry in scenes), "oral scene pool did not contribute")
location_override = {"enabled": True, "apply_mode": "replace", "scene_entries": ["custom scene"]}
composition_override = {"enabled": True, "apply_mode": "replace", "composition_entries": ["custom composition"]}
_expect(
pb._scene_pool(category, subcategory, item, "configured_cast", location_override)
== row_pools.scene_pool(category, subcategory, item, "configured_cast", location_override),
"Prompt builder scene pool should delegate to row_pools",
)
_expect(
pb._expression_pool(category, subcategory, item) == row_pools.expression_pool(category, subcategory, item),
"Prompt builder expression pool should delegate to row_pools",
)
_expect(
pb._pose_pool(category, subcategory, item, "couple", "standard") == row_pools.pose_pool(category, subcategory, item, "couple", "standard"),
"Prompt builder pose pool should delegate to row_pools",
)
_expect(
pb._composition_pool(category, subcategory, item, "configured_cast", composition_override)
== row_pools.composition_pool(category, subcategory, item, "configured_cast", composition_override),
"Prompt builder composition pool should delegate to row_pools",
)
def smoke_category_subcategory_matrix() -> None:
categories = category_library.load_category_library()
cases: list[tuple[dict[str, Any], dict[str, Any]]] = []
for category in categories:
for subcategory in category.get("subcategories") or []:
if subcategory.get("item_templates") or subcategory.get("items"):
cases.append((category, subcategory))
_expect(len(cases) >= 30, "category matrix should cover all configured JSON subcategories")
hardcore_filter = _broad_hardcore_filter()
for index, (category, subcategory) in enumerate(cases, start=6101):
category_slug = str(category.get("slug") or "")
subcategory_slug = str(subcategory.get("slug") or "")
name = f"category_matrix.{category_slug}.{subcategory_slug}"
women_count, men_count, cast = _matrix_cast_for_route(category, subcategory)
row = _prompt_row(
name=name,
category=str(category.get("name") or ""),
subcategory=_exact_subcategory_selector(category, subcategory),
seed=index,
character_cast=cast,
women_count=women_count,
men_count=men_count,
hardcore_position_config=hardcore_filter if category_slug == "hardcore_sexual_poses" else "",
)
_expect(row.get("source") == "json_category", f"{name}.source should be json_category")
_expect(row.get("category_slug") == category_slug, f"{name}.category_slug drifted to {row.get('category_slug')}")
_expect(
row.get("subcategory_slug") == subcategory_slug,
f"{name}.subcategory_slug drifted to {row.get('subcategory_slug')}",
)
_expect_text(f"{name}.item", row.get("item"), 8)
_expect_text(f"{name}.scene_text", row.get("scene_text"), 8)
_expect_text(f"{name}.composition", row.get("composition"), 8)
_expect(isinstance(row.get("item_axis_values"), dict), f"{name}.item_axis_values missing")
_expect(isinstance(row.get("formatter_hints"), dict), f"{name}.formatter_hints missing")
if category_slug == "hardcore_sexual_poses":
_expect(row.get("content_seed_axis") == "pose", f"{name}.content_seed_axis should be pose")
_expect_text(f"{name}.source_role_graph", row.get("source_role_graph") or row.get("role_graph"), 20)
_expect_text(f"{name}.action_family", row.get("action_family"), 3)
_expect_text(f"{name}.position_family", row.get("position_family"), 3)
_expect(isinstance(row.get("position_keys"), list), f"{name}.position_keys missing")
_expect(isinstance(row.get("item_template_metadata"), dict), f"{name}.item_template_metadata missing")
_expect(row.get("item_template_metadata"), f"{name}.item_template_metadata should not be empty")
else:
_expect(row.get("content_seed_axis") == "content", f"{name}.content_seed_axis should be content")
_expect_formatter_outputs(row, name, target="single")
def smoke_hardcore_category_routes() -> None:
cast = _character_cast()
cases = [
("hardcore_penetration", "Penetrative sex", "penetration_only", "penetrative", {"penetration", "default"}, "penetrative sex", "penetrative action"),
("hardcore_oral", "Oral sex", "oral_only", "oral", {"oral"}, "oral sex", "oral action"),
("hardcore_manual", "Manual stimulation", "manual_only", "manual", {"manual"}, "manual stimulation", "manual action"),
("hardcore_anal", "Anal and double penetration", "anal_only", "anal", {"anal", "toy_double"}, "anal sex", "anal action"),
("hardcore_outercourse", "Outercourse and genital teasing", "outercourse_only", "outercourse", {"outercourse"}, "outercourse", "non-penetrative action"),
("hardcore_foreplay", "Foreplay and teasing", "foreplay_only", "foreplay", {"foreplay"}, "foreplay", "foreplay action"),
("hardcore_aftercare", "Aftercare and cleanup", "interaction_only", "interaction", {"foreplay"}, "interaction", "interaction beat"),
]
for index, (name, subcategory, focus, position_family, action_families, sdxl_tag, caption_label) in enumerate(cases, start=1101):
row = _prompt_row(
name=name,
category="Hardcore sexual poses",
subcategory=subcategory,
seed=index,
character_cast=cast,
women_count=1,
men_count=1,
hardcore_position_config=_action_filter(focus),
)
_expect_custom_row(row, name)
_expect(row.get("subject_type") == "configured_cast", f"{name} should use configured cast")
_expect(row.get("position_family") == position_family, f"{name} position_family mismatch: {row.get('position_family')}")
_expect(row.get("action_family") in action_families, f"{name} action_family mismatch: {row.get('action_family')}")
_expect(isinstance(row.get("position_keys"), list), f"{name} position_keys missing")
_expect_formatter_outputs(row, name, target="single")
sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(row), target="single", trigger=SdxlTrigger, prepend_trigger=True)
_expect(sdxl_tag in (sdxl.get("sdxl_prompt") or "").lower(), f"{name} SDXL prompt did not include family tag {sdxl_tag!r}")
caption, _method = caption_naturalizer.naturalize_caption("", metadata_json=_json(row), trigger=Trigger, include_trigger=True)
_expect(caption_label in caption.lower(), f"{name} caption did not include family label {caption_label!r}")
styled_row = _prompt_row(
name="hardcore_style_pool_override",
category="Hardcore sexual poses",
subcategory="Penetrative sex",
seed=1181,
character_cast=cast,
women_count=1,
men_count=1,
hardcore_position_config=_action_filter("penetration_only"),
style_config=pb.build_style_config_json(preset="comic_pinup_colored_pencil"),
)
_expect("comic pin-up" in styled_row.get("style", ""), "Style Pool did not reach generated hardcore row style")
_expect("comic linework" in styled_row.get("positive_suffix", ""), "Style Pool did not reach generated hardcore row suffix")
_expect("comic pin-up" in styled_row.get("prompt", ""), "Style Pool style was not rendered into hardcore prompt")
multi_cases = [
("hardcore_threesome", "Threesomes", "threesome_only", "threesome", {"threesome", "toy_double"}, "threesome", "three-person action", 1, 2),
("hardcore_group", "Group sex and orgy", "group_only", "group", {"group", "toy_double"}, "group sex", "group action", 2, 2),
]
for index, (name, subcategory, focus, position_family, action_families, sdxl_tag, caption_label, women_count, men_count) in enumerate(multi_cases, start=1151):
subjects = ["woman"] * women_count + ["man"] * men_count
row = _prompt_row(
name=name,
category="Hardcore sexual poses",
subcategory=subcategory,
seed=index,
character_cast=_character_cast_subjects(subjects),
women_count=women_count,
men_count=men_count,
hardcore_position_config=_action_filter(focus),
)
_expect_custom_row(row, name)
_expect(row.get("subject_type") == "configured_cast", f"{name} should use configured cast")
_expect(row.get("position_family") == position_family, f"{name} position_family mismatch: {row.get('position_family')}")
_expect(row.get("action_family") in action_families, f"{name} action_family mismatch: {row.get('action_family')}")
_expect_formatter_outputs(row, name, target="single")
sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(row), target="single", trigger=SdxlTrigger, prepend_trigger=True)
_expect(sdxl_tag in (sdxl.get("sdxl_prompt") or "").lower(), f"{name} SDXL prompt did not include family tag {sdxl_tag!r}")
caption, _method = caption_naturalizer.naturalize_caption("", metadata_json=_json(row), trigger=Trigger, include_trigger=True)
_expect(caption_label in caption.lower(), f"{name} caption did not include family label {caption_label!r}")
annotated_row = None
for seed in range(1801, 1841):
row = _prompt_row(
name="hardcore_annotated_template",
category="Hardcore sexual poses",
subcategory="Oral sex",
seed=seed,
character_cast=cast,
women_count=1,
men_count=1,
hardcore_position_config=_action_filter("oral_only"),
)
if row.get("item_template_metadata"):
annotated_row = row
break
_expect(annotated_row is not None, "No annotated item template reached generated row in deterministic seed window")
if annotated_row is not None:
_expect(annotated_row.get("action_family") == "oral", "Annotated item template action_family did not reach row")
_expect(annotated_row.get("position_family") == "oral", "Annotated item template position_family did not reach row")
_expect(annotated_row.get("item_template_metadata", {}).get("action_family") == "oral", "Annotated item metadata missing in row")
def smoke_krea_close_foreplay_route() -> None:
row = _prompt_row(
name="krea_close_foreplay_route",
category="Hardcore sexual poses",
subcategory="Foreplay and teasing",
seed=3401,
character_cast=_character_cast(),
women_count=1,
men_count=1,
hardcore_position_config=_action_filter("foreplay_only"),
)
_expect_custom_row(row, "krea_close_foreplay_route")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
prompt = _expect_text("krea_close_foreplay_route.krea_prompt", krea.get("krea_prompt"), 40)
lower = prompt.lower()
_expect("metadata" in krea.get("method", ""), "close foreplay route did not use metadata")
_expect("role graph:" not in lower, "close foreplay leaked raw role label")
_expect("foreplay action:" not in lower, "close foreplay leaked raw item label")
_expect("on against" not in lower, "close foreplay kept invalid surface grammar")
_expect(
any(term in lower for term in ("clothing", "hands", "kiss", "bodies press", "body contact")),
"close foreplay lost close-contact action wording",
)
_expect_formatter_outputs(row, "krea_close_foreplay_route", target="single")
def _insta_options(**overrides: Any) -> str:
options = pb.build_insta_of_options_json(
softcore_cast="same_as_hardcore",
hardcore_cast="couple",
hardcore_women_count=1,
hardcore_men_count=1,
softcore_level="lingerie_tease",
hardcore_level="hardcore",
platform_style="hybrid",
continuity="same_creator_same_room",
hardcore_clothing_continuity="explicit_nude",
softcore_camera_mode="standard",
hardcore_camera_mode="standard",
camera_detail="compact",
hardcore_detail_density="balanced",
)
data = json.loads(options)
data.update(overrides)
return _json(data)
def smoke_pair_options_policy() -> None:
_expect(
pb.INSTA_OF_SOFTCORE_OUTFITS is pb.pair_options.INSTA_OF_SOFTCORE_OUTFITS,
"prompt_builder should delegate Insta/OF softcore outfit policy to pair_options",
)
_expect(
pb.HARDCORE_DETAIL_DENSITY_CHOICES is pb.pair_options.HARDCORE_DETAIL_DENSITY_CHOICES,
"prompt_builder should delegate hardcore detail density choices to pair_options",
)
_expect(
pb.pair_options.hardcore_detail_directive("compact").startswith("Use one compact"),
"compact hardcore detail density should have a compact directive",
)
_expect(
pb.pair_options.hardcore_detail_directive("dense").startswith("Use dense"),
"dense hardcore detail density should have a dense directive",
)
_expect(
pb.pair_options.hardcore_detail_directive("balanced") == "",
"balanced hardcore detail density should not add a directive",
)
_expect(
pb.pair_options.hardcore_detail_directive("bad") == "",
"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",
)
sanitized_closet_scene = pair_clothing.body_exposure_scene_text(
"full-length closet mirror with outfit racks, shoe shelves, and soft boutique lighting"
).lower()
_expect("outfit racks" not in sanitized_closet_scene, "Pair clothing body exposure scene cleanup should remove outfit racks")
_expect("shoe shelves" not in sanitized_closet_scene, "Pair clothing body exposure scene cleanup should remove shoe shelves")
_expect("mirror shelves" in sanitized_closet_scene, "Pair clothing body exposure scene cleanup should keep neutral mirror detail")
_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",
)
_expect(
pair_cast.cast_summary_phrase(2, 1) == "2 women, 1 man, 3 total adults",
"Pair cast summary phrase should live in pair_cast",
)
descriptor_row = {
"age": "25-year-old adult",
"body_phrase": "curvy build",
"skin": "warm skin",
"hair": "dark hair",
"eyes": "brown eyes",
}
_expect(
pb._insta_of_descriptor(descriptor_row) == pair_cast.insta_descriptor_from_row(descriptor_row),
"Prompt builder Insta descriptor should delegate to pair_cast",
)
_expect(
pair_cast.insta_descriptor_from_context(
{"subject_type": "man", "age": "40-year-old adult", "body_phrase": "stocky figure"}
)
== "40-year-old adult man, stocky figure",
"Pair cast context descriptor formatting changed",
)
_expect(
pair_cast.prompt_cast_descriptors("Woman A / primary creator: descriptor") == "Woman A: descriptor",
"Pair cast prompt descriptor label cleanup changed",
)
def _fake_character_context(
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, Any], dict[str, Any] | None]:
subject = "man" if label.startswith("Man ") else "woman"
age = "40-year-old adult" if subject == "man" else "30-year-old adult"
return {"subject_type": subject, "age": age, "body_phrase": f"{label} body"}, label_map.get(label)
descriptor_entries, descriptor_slots = pair_cast.cast_descriptor_entries_from_slots(
seed_config={},
seed=1,
row_number=1,
ethnicity="any",
figure="any",
no_plus_women=False,
no_black=False,
women_count=2,
men_count=1,
character_slots=[{"subject_type": "man", "presence_mode": "pov"}],
character_slot_map={"Man A": {"subject_type": "man", "presence_mode": "pov"}},
primary_descriptor="primary descriptor",
axis_rng=lambda _config, _axis, seed_value, row_value: random.Random(seed_value + row_value),
character_context_for_label=_fake_character_context,
slot_is_pov=lambda slot: bool(slot and slot.get("presence_mode") == "pov"),
)
_expect(
descriptor_entries
== [
"Woman A / primary creator: primary descriptor",
"Woman B: 30-year-old adult woman, Woman B body",
],
"Pair cast descriptor entries should keep primary label and skip POV men",
)
_expect(
descriptor_slots == [{"subject_type": "man", "presence_mode": "pov"}],
"Pair cast descriptor entries should return the source slots",
)
partner_styling = pair_cast.softcore_partner_styling(
seed_config={},
seed=1,
row_number=1,
women_count=2,
men_count=1,
pov_labels=["Man A"],
label_map={"Woman B": {"softcore_outfit": "custom satin dress"}, "Man A": {"softcore_outfit": "hidden"}},
axis_rng=lambda _config, _axis, seed_value, row_value: random.Random(seed_value + row_value),
choose=lambda _rng, pool: pool[0],
slot_softcore_outfit=lambda slot, _rng: str((slot or {}).get("softcore_outfit") or ""),
)
_expect(
partner_styling["outfits"] == ["Woman B wears custom satin dress"],
"Pair cast partner styling should use configured partner outfit and skip POV men",
)
_expect_text("pair_cast.partner_pose", partner_styling.get("pose"), 12)
options = json.loads(
pb.build_insta_of_options_json(
softcore_expression_enabled="false",
hardcore_expression_enabled="0",
softcore_expression_intensity=1.4,
hardcore_expression_intensity=-0.4,
hardcore_detail_density="invalid",
)
)
_expect(options["softcore_expression_enabled"] is False, "softcore expression enabled should normalize false strings")
_expect(options["hardcore_expression_enabled"] is False, "hardcore expression enabled should normalize false strings")
_expect(options["softcore_expression_intensity"] == 1.0, "softcore expression intensity should clamp high values")
_expect(options["hardcore_expression_intensity"] == 0.0, "hardcore expression intensity should clamp low values")
_expect(options["hardcore_detail_density"] == "balanced", "invalid hardcore detail density should fallback")
parsed = pb._parse_insta_of_options(
{
"softcore_cast": "bad",
"hardcore_cast": "bad",
"softcore_camera_mode": "bad",
"hardcore_camera_mode": "bad",
"camera_detail": "bad",
"hardcore_detail_density": "bad",
"hardcore_women_count": "20",
"hardcore_men_count": "-3",
}
)
_expect(parsed["softcore_cast"] == "solo", "invalid softcore cast should fallback")
_expect(parsed["hardcore_cast"] == "use_counts", "invalid hardcore cast should fallback")
_expect(parsed["softcore_camera_mode"] == "handheld_selfie", "invalid softcore camera should fallback")
_expect(parsed["hardcore_camera_mode"] == "from_camera_config", "invalid hardcore camera should fallback")
_expect(parsed["camera_detail"] == "from_camera_config", "invalid camera detail should fallback")
_expect(parsed["hardcore_detail_density"] == "balanced", "invalid hardcore density should fallback on parse")
_expect(parsed["hardcore_women_count"] == 12, "women count should clamp to max")
_expect(parsed["hardcore_men_count"] == 0, "men count should clamp to min")
_expect(pb.character_softcore_outfit_values("partner_man"), "partner man softcore outfit pool should not be empty")
_expect(
pb.character_softcore_outfit_values("custom", "one; two\nthree") == ["one", "two", "three"],
"custom softcore outfits should split stable free-text lists",
)
_expect("fully nude" in pb.character_hardcore_clothing_values("fully_nude"), "fully nude clothing state should be exposed")
_expect(
pb.character_hardcore_clothing_values("custom", "bare; outfit pushed aside") == ["bare", "outfit pushed aside"],
"custom hardcore clothing should split stable free-text lists",
)
_expect(pb._insta_of_hardcore_counts({"hardcore_cast": "threesome"}) == (2, 1), "threesome count policy changed")
_expect(pb._insta_of_softcore_category("social_tease") == ("Casual clothes", "Casual clothes / Smart casual"), "softcore category mapping changed")
def smoke_pair_route_policy() -> None:
def _fake_build_prompt(**kwargs: Any) -> dict[str, Any]:
is_hard = kwargs.get("category") == "Hardcore sexual poses"
return {
"category": kwargs.get("category"),
"subcategory": kwargs.get("subcategory"),
"scene_text": "shared test room",
"source_scene_text": "",
"composition": "centered test composition",
"source_composition": "",
"expression": "steady look",
"role_graph": "test role graph",
"item": "test item",
"positive_suffix": "test positive suffix",
"negative_prompt": "test negative",
"prompt": "test hard prompt" if is_hard else "test soft prompt",
"caption": "test hard caption" if is_hard else "test soft caption",
}
pair_options_data = {
"softcore_cast": "solo",
"softcore_expression_enabled": True,
"softcore_expression_intensity": 0.45,
"hardcore_expression_enabled": True,
"hardcore_expression_intensity": 0.85,
"hardcore_detail_density": "balanced",
}
pair_rows_kwargs: dict[str, Any] = {
"row_number": 1,
"start_index": 1,
"seed": 10,
"active_trigger": Trigger,
"parsed_seed_config": {},
"options": pair_options_data,
"ethnicity": "any",
"figure": "random",
"no_plus_women": False,
"no_black": False,
"character_profile": "",
"character_cast": "",
"character_slot_map": {},
"pov_character_labels": [],
"hard_women_count": 1,
"hard_men_count": 1,
"soft_category": "Casual clothes",
"soft_subcategory": "Casual clothes / Smart casual",
"softcore_level_key": "social_tease",
"hardcore_random_subcategory": pb.RANDOM_SUBCATEGORY,
"hardcore_position_config": "",
"location_config": "",
"composition_config": "",
"build_prompt": _fake_build_prompt,
"axis_rng": lambda _config, _axis, seed_value, row_value: random.Random(seed_value + row_value),
"cast_expression_intensity_override": lambda value, _slots, _women, _men, _phase: (value, "input"),
"context_from_character_slot": lambda *_args, **_kwargs: {},
"apply_character_context_to_row": lambda row, context: {**row, **context},
"disable_row_expression": lambda row, source: {**row, "expression_disabled": True, "expression_intensity_source": source},
"slot_softcore_outfit": lambda _slot, _rng: "",
"softcore_outfit": lambda _rng, _level: "test soft outfit",
"softcore_pose": lambda _rng, _level: "test soft pose",
"softcore_item_prompt_label": lambda _level: "Softcore test outfit",
"pov_prompt_directive": lambda labels: "POV directive" if labels else "",
"pov_composition_prompt": lambda composition, labels: f"{composition} for {','.join(labels)}" if labels else str(composition),
}
rows_route = pair_rows.build_insta_pair_rows_result(**pair_rows_kwargs)
rows_legacy = pair_rows.build_insta_pair_rows(**pair_rows_kwargs)
_expect(rows_route.soft_row == rows_legacy["soft_row"], "Typed pair row route should match legacy soft row")
_expect(rows_route.hard_row == rows_legacy["hard_row"], "Typed pair row route should match legacy hard row")
_expect(
rows_route.hard_content_rng.getstate() == rows_legacy["hard_content_rng"].getstate(),
"Typed pair row route should match legacy hard content RNG state",
)
_expect(rows_route.soft_row["item"] == "test soft outfit", "Typed pair row route lost soft outfit override")
_expect(rows_route.hard_row["hardcore_detail_density"] == "balanced", "Typed pair row route lost hard density")
camera_options = {
"hardcore_camera_mode": "same_as_softcore",
"softcore_camera_mode": "standard",
"camera_detail": "compact",
"softcore_cast": "same_as_hardcore",
"continuity": "same_creator_same_room",
}
def _camera_config_with_mode(source: Any, mode: str) -> dict[str, Any]:
parsed = dict(source or {})
parsed["camera_mode"] = mode
return parsed
def _camera_directive(config: dict[str, Any]) -> tuple[str, dict[str, Any]]:
return f"{config.get('camera_mode')} camera", config
def _camera_rows() -> tuple[dict[str, Any], dict[str, Any]]:
return (
{"scene_text": "soft room", "composition": "soft composition"},
{"scene_text": "hard room", "composition": "hard composition"},
)
camera_common = {
"options": camera_options,
"camera_config": {"base": "camera"},
"softcore_camera_config": None,
"hardcore_camera_config": None,
"hard_women_count": 1,
"hard_men_count": 1,
"pov_character_labels": [],
"camera_detail_choices": ("compact",),
"camera_config_with_mode": _camera_config_with_mode,
"camera_directive": _camera_directive,
"apply_contextual_composition": lambda row, subject_kind: {**row, "subject_kind": subject_kind},
"contextual_composition_prompt": lambda scene, composition, subject_kind: f"{subject_kind}: {scene}: {composition}",
"composition_prompt": lambda composition: f"Framed as {composition}",
"camera_scene_directive_for_context": lambda _scene, _composition, config, _pov, subject: (f"{subject} scene directive", config),
}
soft_row, hard_row = _camera_rows()
camera_route = pair_camera.resolve_insta_pair_camera_result(soft_row=soft_row, hard_row=hard_row, **camera_common)
soft_row, hard_row = _camera_rows()
camera_legacy = pair_camera.resolve_insta_pair_camera(soft_row=soft_row, hard_row=hard_row, **camera_common)
_expect(camera_route.as_dict() == camera_legacy, "Typed pair camera route should match legacy dict route")
_expect(camera_route.hard_scene == "soft room", "Typed pair camera route lost same-room continuity")
_expect(camera_route.hard_camera_sentence.startswith("Camera control:"), "Typed pair camera route lost hard camera sentence")
_expect(camera_route.soft_row.get("subject_kind") == "couple", "Same-cast softcore camera route should use couple subject kind")
_expect("couple scene directive" in camera_route.soft_camera_scene_directive, "Same-cast softcore camera directive should use couple wording")
clothing_common = {
"hard_row": {
"role_graph": "the man thrusts his penis into the woman",
"item": "penetrative test item",
},
"mode": "explicit_nude",
"softcore_outfit": "test lingerie",
"character_hardcore_clothing_entries": [],
"men_count": 0,
"pov_labels": [],
"rng": random.Random(1),
"continuity_map": pb.INSTA_OF_HARDCORE_CLOTHING_CONTINUITY,
"choose": lambda _rng, pool: pool[0],
}
clothing_route = pair_clothing.resolve_hardcore_pair_clothing_result(**clothing_common)
clothing_legacy = pair_clothing.resolve_hardcore_pair_clothing(**clothing_common)
_expect(clothing_route.as_dict() == clothing_legacy, "Typed pair clothing route should match legacy dict route")
_expect(clothing_route.woman_access == "lower", "Typed pair clothing route lost lower-access detection")
_expect(clothing_route.requires_body_exposure_scene is True, "Typed pair clothing route lost exposure-scene flag")
partial_common = {**clothing_common, "mode": "partially_removed"}
partial_route = pair_clothing.resolve_hardcore_pair_clothing_result(**partial_common)
_expect(partial_route.requires_body_exposure_scene is True, "Partial lower-access clothing should request scene cleanup")
structured_axis_clothing = pair_clothing.resolve_hardcore_pair_clothing_result(
**{
**clothing_common,
"hard_row": {
"role_graph": "generic adult action",
"item": "generic configured action",
"item_axis_values": {
"position": {"text": "edge-supported penetration"},
"leg_detail": ["thighs open", "auto"],
},
},
"mode": "partially_removed",
}
)
_expect(
structured_axis_clothing.woman_access == "lower",
"Pair clothing should read structured/list axis values for lower-access detection",
)
oral_common = {
**clothing_common,
"hard_row": {
"role_graph": "the woman takes the man's penis in her mouth",
"item": "standing oral test item",
},
"mode": "partially_removed",
}
oral_route = pair_clothing.resolve_hardcore_pair_clothing_result(**oral_common)
_expect(oral_route.requires_body_exposure_scene is True, "Man lower-access clothing should request scene cleanup")
def smoke_pair_builder_policy() -> None:
request = pair_builder.InstaPairBuildRequest(
row_number=1,
start_index=1,
seed=2101,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
)
built = pair_builder.build_insta_of_pair(request, pb._insta_pair_build_dependencies())
delegated = pb.build_insta_of_pair(
row_number=request.row_number,
start_index=request.start_index,
seed=request.seed,
ethnicity=request.ethnicity,
figure=request.figure,
no_plus_women=request.no_plus_women,
no_black=request.no_black,
trigger=request.trigger,
prepend_trigger_to_prompt=request.prepend_trigger_to_prompt,
seed_config=request.seed_config,
options_json=request.options_json,
filter_config=request.filter_config,
camera_config=request.camera_config,
softcore_camera_config=request.softcore_camera_config,
hardcore_camera_config=request.hardcore_camera_config,
character_profile=request.character_profile,
character_cast=request.character_cast,
hardcore_position_config=request.hardcore_position_config,
location_config=request.location_config,
composition_config=request.composition_config,
extra_positive=request.extra_positive,
extra_negative=request.extra_negative,
)
_expect(built == delegated, "Prompt builder Insta/OF wrapper should delegate to pair_builder without output drift")
_expect_pair(built, "pair_builder_policy")
def _expect_pair(pair: dict[str, Any], name: str) -> None:
_expect(pair.get("mode") == "Insta/OF", f"{name}.mode should be Insta/OF")
_expect_row_base(pair.get("softcore_row") or {}, f"{name}.softcore_row")
_expect_custom_row(pair.get("hardcore_row") or {}, f"{name}.hardcore_row")
_expect_text(f"{name}.shared_descriptor", pair.get("shared_descriptor"), 12)
_expect(pair.get("shared_cast_descriptors"), f"{name}.shared_cast_descriptors should not be empty")
shared_cast_text = "; ".join(str(item).strip() for item in pair.get("shared_cast_descriptors") or [] if str(item).strip())
_expect(
pair["hardcore_row"].get("cast_descriptor_text") == shared_cast_text,
f"{name}.hardcore_row cast descriptors drifted from pair root",
)
if pair.get("options", {}).get("softcore_cast") == "same_as_hardcore":
_expect(
pair["softcore_row"].get("cast_descriptor_text") == shared_cast_text,
f"{name}.softcore_row cast descriptors drifted from same-cast pair root",
)
_expect_text(f"{name}.softcore_prompt", pair.get("softcore_prompt"), 20)
_expect_text(f"{name}.hardcore_prompt", pair.get("hardcore_prompt"), 20)
_expect_trigger_once(f"{name}.softcore_prompt", pair.get("softcore_prompt"), Trigger)
_expect_trigger_once(f"{name}.hardcore_prompt", pair.get("hardcore_prompt"), Trigger)
_expect_trigger_once(f"{name}.softcore_caption", pair.get("softcore_caption"), Trigger)
_expect_trigger_once(f"{name}.hardcore_caption", pair.get("hardcore_caption"), Trigger)
_expect(pair["softcore_row"].get("prompt") == pair.get("softcore_prompt"), f"{name}.softcore_row prompt drifted from pair prompt")
_expect(pair["hardcore_row"].get("prompt") == pair.get("hardcore_prompt"), f"{name}.hardcore_row prompt drifted from pair prompt")
_expect(pair["softcore_row"].get("caption") == pair.get("softcore_caption"), f"{name}.softcore_row caption drifted from pair caption")
_expect(pair["hardcore_row"].get("caption") == pair.get("hardcore_caption"), f"{name}.hardcore_row caption drifted from pair caption")
_expect(
pair["softcore_row"].get("negative_prompt") == pair.get("softcore_negative_prompt"),
f"{name}.softcore_row negative drifted from pair negative",
)
_expect(
pair["hardcore_row"].get("negative_prompt") == pair.get("hardcore_negative_prompt"),
f"{name}.hardcore_row negative drifted from pair negative",
)
if "softcore_partner_styling" in pair:
_expect(
pair["softcore_row"].get("softcore_partner_styling") == pair.get("softcore_partner_styling"),
f"{name}.softcore_row partner styling drifted from pair root",
)
for key in (
"hardcore_clothing_state",
"character_hardcore_clothing",
"default_man_hardcore_clothing",
"hardcore_detail_density",
"hardcore_position_config",
):
if key in pair:
_expect(pair["hardcore_row"].get(key) == pair.get(key), f"{name}.hardcore_row {key} drifted from pair root")
_expect_no_duplicate_comma_items(f"{name}.softcore_negative", pair.get("softcore_negative_prompt"))
_expect_no_duplicate_comma_items(f"{name}.hardcore_negative", pair.get("hardcore_negative_prompt"))
_expect_formatter_outputs(pair, name, target="softcore")
_expect_formatter_outputs(pair, f"{name}.hardcore", target="hardcore")
def smoke_insta_pair() -> None:
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=2101,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
)
_expect_pair(pair, "insta_pair_same_cast")
_expect(pair["softcore_row"].get("scene_text") == pair["hardcore_row"].get("scene_text"), "pair scene continuity broke")
_expect_no_softcore_noise("insta_pair_same_cast.softcore_prompt", pair.get("softcore_prompt"))
_expect("styled creator-teaser frame" in str(pair.get("softcore_prompt", "")).lower(), "pair softcore prompt lost clean cast-presence wording")
krea_soft = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="softcore")
krea_soft_prompt = _expect_text("insta_pair_same_cast.krea_soft_prompt", krea_soft.get("krea_prompt"), 40)
_expect_no_softcore_noise("insta_pair_same_cast.krea_soft_prompt", krea_soft_prompt)
_expect("styled creator-teaser frame" in krea_soft_prompt.lower(), "Krea softcore prompt lost clean same-cast wording")
clothing_state = _clean_key(pair.get("hardcore_clothing_state"))
_expect("body is fully exposed" in clothing_state, "explicit nude pair should keep body exposure state")
_expect("teaser outfit detail" not in clothing_state, "explicit nude pair should not repeat softcore outfit detail")
partner_styling = pair.get("softcore_partner_styling") or {}
_expect(partner_styling.get("outfits"), "same-cast pair should keep partner softcore outfit styling")
_expect_text("insta_pair_same_cast.partner_pose", partner_styling.get("pose"), 12)
def smoke_krea_pair_clothing_state() -> None:
forced_location = pb.build_location_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_locations="closet_scene: full-length closet mirror with outfit racks, shoe shelves, and soft boutique lighting",
)
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3511,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(hardcore_clothing_continuity="partially_removed"),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
location_config=forced_location,
)
_expect_pair(pair, "krea_pair_clothing_state")
typed_route = krea_pair_formatter.format_insta_pair_result(
krea_pair_formatter.KreaPairFormatRequest(pair, "balanced", "preserve"),
krea_formatter._krea_pair_format_dependencies(),
)
legacy_route = krea_formatter._insta_pair_to_krea(pair, "balanced", "preserve")
_expect(
typed_route.as_tuple() == legacy_route,
"Typed Krea pair formatter route should match legacy wrapper output",
)
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text("krea_pair_clothing_state.krea_prompt", krea.get("krea_prompt"), 60)
lower = prompt.lower()
root_clothing = _clean_key(pair.get("hardcore_clothing_state"))
_expect("lower body is clear" in root_clothing, "pair root clothing state lost lower-body access wording")
_expect(pair.get("default_man_hardcore_clothing"), "pair root default man hardcore clothing is missing")
_expect("metadata" in krea.get("method", ""), "pair clothing route did not use metadata")
_expect("clothing state:" not in lower, "Krea clothing route leaked raw clothing label")
_expect("visual clothing state" not in lower, "Krea clothing route fell back to visual clothing state label")
_expect("softcore outfit" not in lower and "teaser outfit" not in lower, "Krea clothing route leaked softcore outfit label")
_expect("lower body is clear" in lower, "Krea clothing route lost generated clothing continuity")
_expect("the man keeps" in lower, "Krea clothing route lost partner clothing continuity")
_expect("outfit racks" not in lower and "shoe shelves" not in lower, "Krea pair formatter leaked unsanitized hard scene")
hard_scene = _clean_key(pair["hardcore_row"].get("scene_text"))
_expect("outfit racks" not in hard_scene and "shoe shelves" not in hard_scene, "Pair builder leaked outfit-check scene clutter into hardcore row")
_expect("mirror shelves" in hard_scene, "Pair builder lost neutralized scene anchor")
caption_text, _caption_method = caption_naturalizer.naturalize_caption("", metadata_json=_json(pair), target="hardcore")
caption_lower = caption_text.lower()
_expect("outfit racks" not in caption_lower and "shoe shelves" not in caption_lower, "Caption pair formatter leaked unsanitized hard scene")
sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(pair), target="hardcore")
sdxl_lower = sdxl.get("sdxl_prompt", "").lower()
_expect("outfit racks" not in sdxl_lower and "shoe shelves" not in sdxl_lower, "SDXL pair formatter leaked unsanitized hard scene")
def smoke_krea_anal_axis_compatibility() -> None:
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=6252,
ethnicity="french_european",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(hardcore_clothing_continuity="partially_removed", camera_detail="off"),
character_cast=_character_cast(),
hardcore_position_config=_action_filter(
"anal_only",
pb.build_hardcore_position_pool_json(family="anal"),
),
seed_config=pb.build_seed_lock_config_json(base_seed=6252, reroll_axis="pose", reroll_seed=6352),
)
hard_row = pair["hardcore_row"]
axis_values = hard_row.get("item_axis_values") or {}
position = str(axis_values.get("position") or "").lower()
leg_detail = str(axis_values.get("leg_detail") or "").lower()
if "side-lying" in position:
_expect("standing" not in leg_detail, "Generated side-lying anal row leaked standing leg detail")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text("krea_anal_axis_compatibility.krea_prompt", krea.get("krea_prompt"), 60)
lower = prompt.lower()
_expect(
"side-lying rear-entry anal pose" not in lower or "stands braced" not in lower,
"Krea anal formatter mixed side-lying anchor with standing role graph",
)
_expect("side-lying anal position, standing with legs braced" not in lower, "Krea anal formatter leaked contradictory axis detail")
def smoke_insta_pair_pov() -> None:
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=2201,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(),
character_cast=_character_cast(pov_man=True),
hardcore_position_config=_action_filter("oral_only"),
)
_expect_pair(pair, "insta_pair_pov_man")
pov_labels = pair.get("pov_character_labels") or []
_expect("Man A" in pov_labels, "pair POV labels should include Man A")
hard_row = pair.get("hardcore_row") or {}
_expect("Man A" in (hard_row.get("pov_character_labels") or []), "hard row POV labels should include Man A")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = krea.get("krea_prompt") or ""
_expect("viewer" in prompt.lower(), "POV Krea prompt should mention viewer perspective")
def smoke_insta_pair_camera_split() -> None:
soft_camera = _orbit_camera(
horizontal_angle=45,
vertical_angle=-30,
zoom=5.0,
subject_focus="environment",
)
hard_camera = _orbit_camera(
horizontal_angle=135,
vertical_angle=30,
zoom=8.0,
subject_focus="action",
)
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=2251,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(
softcore_camera_mode="from_camera_config",
hardcore_camera_mode="from_camera_config",
camera_detail="compact",
),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
location_config=_coworking_location_config(),
softcore_camera_config=soft_camera,
hardcore_camera_config=hard_camera,
)
_expect_pair(pair, "insta_pair_camera_split")
soft_scene = _expect_text("insta_pair_camera_split.soft_camera_scene", pair.get("softcore_camera_scene_directive"), 40)
hard_scene = _expect_text("insta_pair_camera_split.hard_camera_scene", pair.get("hardcore_camera_scene_directive"), 40)
_expect("front-right quarter view" in soft_scene, "soft camera scene missed soft orbit direction")
_expect("back-right quarter view" in hard_scene, "hard camera scene missed hard orbit direction")
_expect("low-angle shot" in soft_scene, "soft camera scene missed soft elevation")
_expect("elevated shot" in hard_scene, "hard camera scene missed hard elevation")
_expect("near desk edge" in soft_scene, "non-POV camera scene lost coworking foreground anchor")
_expect("laptop corner" in soft_scene, "non-POV camera scene lost coworking foreground detail")
_expect("front-right quarter view" in str(pair.get("softcore_camera_directive")), "soft pair camera directive was not preserved")
_expect("back-right quarter view" in str(pair.get("hardcore_camera_directive")), "hard pair camera directive was not preserved")
soft_row = pair.get("softcore_row") or {}
hard_row = pair.get("hardcore_row") or {}
_expect(pair.get("softcore_camera_config") == soft_row.get("camera_config"), "soft pair camera config drifted from soft row")
_expect(pair.get("hardcore_camera_config") == hard_row.get("camera_config"), "hard pair camera config drifted from hard row")
_expect(pair.get("softcore_camera_directive") == soft_row.get("camera_directive"), "soft pair camera directive drifted from soft row")
_expect(pair.get("hardcore_camera_directive") == hard_row.get("camera_directive"), "hard pair camera directive drifted from hard row")
_expect(pair.get("softcore_camera_scene_directive") == soft_row.get("camera_scene_directive"), "soft pair camera scene drifted from soft row")
_expect(pair.get("hardcore_camera_scene_directive") == hard_row.get("camera_scene_directive"), "hard pair camera scene drifted from hard row")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="auto")
_expect("front-right quarter view" in (krea.get("krea_softcore_prompt") or ""), "Krea soft pair lost soft camera geometry")
_expect("back-right quarter view" in (krea.get("krea_hardcore_prompt") or ""), "Krea hard pair lost hard camera geometry")
def smoke_pov_camera_scene() -> None:
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=2261,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(
softcore_camera_mode="from_camera_config",
hardcore_camera_mode="from_camera_config",
camera_detail="compact",
),
character_cast=_character_cast(pov_man=True),
hardcore_position_config=_action_filter("oral_only"),
location_config=_coworking_location_config(),
hardcore_camera_config=_orbit_camera(
horizontal_angle=135,
vertical_angle=30,
zoom=8.0,
subject_focus="action",
),
)
_expect_pair(pair, "pov_camera_scene")
hard_row = pair.get("hardcore_row") or {}
_expect(not hard_row.get("camera_directive"), "POV hard row should suppress normal camera directive")
scene_directive = _expect_text("pov_camera_scene.hard_camera_scene", hard_row.get("camera_scene_directive"), 40)
_expect("from POV" in scene_directive, "POV camera scene should be marked as first-person")
_expect("not in the lower foreground" in scene_directive, "POV camera scene should keep location anchors out of lower foreground")
_expect(
"near desk edge" not in scene_directive and "laptop corner" not in scene_directive and "chair back" not in scene_directive,
"POV camera scene should not reuse coworking foreground anchors as viewer-side objects",
)
_expect(
"lower foreground is reserved for POV body or hand cues" in scene_directive,
"POV camera scene should reserve the lower foreground for first-person body cues",
)
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = krea.get("krea_prompt") or ""
_expect("from POV" in prompt, "Krea POV prompt lost camera-scene directive")
_expect("laptop corner" not in prompt, "Krea POV prompt leaked foreground desk anchor into camera scene")
_expect("lower foreground is reserved for POV body or hand cues" in prompt, "Krea POV prompt lost POV foreground reservation")
_expect("Camera:" not in prompt, "Krea POV prompt should not emit normal third-person camera directive")
def smoke_krea_pov_penetration_route() -> None:
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3411,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(
softcore_camera_mode="from_camera_config",
hardcore_camera_mode="from_camera_config",
camera_detail="compact",
),
character_cast=_character_cast(pov_man=True),
hardcore_position_config=_action_filter("penetration_only"),
location_config=_coworking_location_config(),
hardcore_camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=0,
zoom=5.5,
subject_focus="action",
),
)
_expect_pair(pair, "krea_pov_penetration_route")
hard_row = pair.get("hardcore_row") or {}
_expect("Man A" in (hard_row.get("pov_character_labels") or []), "POV penetration hard row lost Man A POV label")
_expect(not hard_row.get("camera_directive"), "POV penetration should suppress normal camera directive")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text("krea_pov_penetration_route.krea_prompt", krea.get("krea_prompt"), 60)
lower = prompt.lower()
_expect("metadata" in krea.get("method", ""), "POV penetration route did not use metadata")
_expect("viewer" in lower and "first-person" in lower, "POV penetration lost first-person wording")
_expect("penetrates" in lower or "penetration" in lower, "POV penetration lost penetration action wording")
_expect("woman" in lower and "thigh" in lower, "POV penetration lost body-position anchors")
_expect("camera:" not in prompt, "POV penetration emitted normal third-person camera directive")
_expect("role graph:" not in lower and "sexual scene:" not in lower, "POV penetration leaked raw prompt labels")
style_index = lower.find("explicit consensual")
_expect(
style_index > 0 and lower[:style_index].rstrip().endswith("."),
"POV penetration composition sentence should keep punctuation before style suffix",
)
def smoke_pov_outercourse_position_routes() -> None:
cases = [
(
"pov_outercourse_boobjob",
"boobjob",
("breasts inward around", "directly above the glans"),
("push her breasts inward around", "directly above the glans", "held between her breasts"),
),
(
"pov_outercourse_testicle",
"testicle_sucking",
("face below the pov viewer's penis at testicle height", "penis points upward"),
("face is below the viewer's penis at testicle height", "mouth and tongue licking", "penis points upward"),
),
(
"pov_outercourse_penis_licking",
"penis_licking",
("head low under the pov viewer's penis", "tongue touches the underside"),
("tongue touches the underside", "glans at the tip", "viewer"),
),
(
"pov_outercourse_handjob",
"handjob",
("one hand grips and strokes the pov viewer's penis", "strokes toward the glans"),
("one hand grips and strokes the viewer's penis", "thumb and fingers", "strokes toward the glans"),
),
(
"pov_outercourse_footjob",
"footjob",
("both soles wrapped around the pov viewer's penis", "lower foreground"),
("soles wrap around", "penis shaft", "lower foreground"),
),
]
for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3601):
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=offset,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(
softcore_camera_mode="from_camera_config",
hardcore_camera_mode="from_camera_config",
camera_detail="compact",
),
character_cast=_character_cast(pov_man=True),
hardcore_position_config=_position_filter("outercourse_only", "outercourse", [position_key]),
location_config=_coworking_location_config(),
hardcore_camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=0,
zoom=7.5,
subject_focus="action",
),
)
_expect_pair(pair, name)
hard_row = pair.get("hardcore_row") or {}
_expect(hard_row.get("action_family") == "outercourse", f"{name} action_family should be outercourse")
_expect(hard_row.get("position_family") == "outercourse", f"{name} position_family should be outercourse")
_expect(hard_row.get("position_key") == position_key, f"{name} selected position should be primary position_key")
_expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
for term in role_terms:
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("viewer" in prompt and "first-person" in prompt, f"{name} Krea prompt lost POV wording")
_expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
for term in krea_terms:
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
if position_key == "handjob":
_expect("behind the penis shaft" not in prompt, f"{name} Krea prompt kept vague shaft-only wording: {prompt}")
if position_key == "testicle_sucking":
_expect("head is tucked under the penis shaft" not in prompt, f"{name} Krea prompt kept high-head testicle wording: {prompt}")
def smoke_pov_oral_position_routes() -> None:
cases = [
(
"pov_oral_kneeling",
"kneeling",
("viewer's penis", "takes the viewer's penis in her mouth"),
("pov kneeling oral position", "viewer stands over her", "head is at penis height", "thighs framing"),
),
(
"pov_oral_face_sitting",
"face_sitting",
("straddling the viewer's face", "pussy directly over the viewer's mouth"),
("close first-person underview", "straddling the viewer's face", "tongue contact visible"),
),
(
"pov_oral_sixty_nine",
"sixty_nine",
("head-to-hips", "viewer's mouth on woman a's pussy"),
("pov sixty-nine oral position", "head-to-hips", "viewer's mouth on the woman's pussy", "lower-foreground body cues aligned"),
),
(
"pov_oral_edge_supported",
"edge_supported",
("raised edge with thighs open", "viewer kneels between her legs"),
("pov raised-edge cunnilingus position", "raised edge with thighs open", "face at pussy height"),
),
(
"pov_oral_side_lying",
"side_lying",
("woman a lies on her side", "viewer lies beside her hips"),
("pov side-lying cunnilingus position", "woman lies on her side", "top thigh lifted", "viewer lies beside her hips"),
),
(
"pov_oral_chair",
"chair_oral",
("viewer sits in a chair", "kneels between his thighs"),
("pov chair oral position", "viewer sits in a chair", "kneels between the viewer's thighs", "head is low at his pelvis"),
),
]
for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3701):
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=offset,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(
softcore_camera_mode="from_camera_config",
hardcore_camera_mode="from_camera_config",
camera_detail="compact",
),
character_cast=_character_cast(pov_man=True),
hardcore_position_config=_position_filter("oral_only", "oral", [position_key]),
location_config=_coworking_location_config(),
hardcore_camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=0,
zoom=7.5,
subject_focus="action",
),
)
_expect_pair(pair, name)
hard_row = pair.get("hardcore_row") or {}
_expect(hard_row.get("action_family") == "oral", f"{name} action_family should be oral")
_expect(hard_row.get("position_family") == "oral", f"{name} position_family should be oral")
_expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
for term in role_terms:
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("viewer" in prompt and "first-person" in prompt, f"{name} Krea prompt lost POV wording")
_expect("viewer lies on the viewer" not in prompt, f"{name} Krea prompt kept recursive POV wording: {prompt}")
_expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
_expect("as he looks down" not in prompt, f"{name} Krea prompt kept unresolved male pronoun: {prompt}")
_expect(
"the woman takes the viewer's penis in her mouth with" not in prompt,
f"{name} Krea prompt repeated oral contact detail after POV rewrite: {prompt}",
)
_expect("edge-supported oral position;" not in prompt, f"{name} Krea prompt kept source oral-position scaffold: {prompt}")
for term in krea_terms:
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
def smoke_pov_penetration_position_routes() -> None:
cases = [
(
"pov_penetration_missionary",
"missionary",
("woman a lies on her back", "man a is above her between her thighs"),
("pov missionary position", "viewer is above her", "penetrates her pussy"),
),
(
"pov_penetration_cowgirl",
"cowgirl",
("woman a straddles man a's hips facing him", "man a lies under her"),
("pov cowgirl position", "viewer lies on his back", "woman straddles his hips"),
),
(
"pov_penetration_reverse_cowgirl",
"reverse_cowgirl",
("woman a straddles man a's hips facing away", "man a lies under her"),
("pov reverse cowgirl position", "facing away", "viewer lies on his back"),
),
(
"pov_penetration_doggy",
"doggy",
("woman a is on all fours", "man a is positioned behind her"),
("ass raised toward the pov viewer", "on all fours", "penetrates her pussy"),
),
(
"pov_penetration_edge_supported",
"edge_supported",
("raised edge", "man a kneels between her thighs"),
("pov raised-edge penetration position", "viewer kneels between her legs", "penetrates her pussy"),
),
(
"pov_penetration_lotus",
"lotus_lap",
("woman a sits in man a's lap", "legs around his hips"),
("pov lotus position", "woman sits in his lap", "penetrates her pussy"),
),
]
for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3801):
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=offset,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(
softcore_camera_mode="from_camera_config",
hardcore_camera_mode="from_camera_config",
camera_detail="compact",
),
character_cast=_character_cast(pov_man=True),
hardcore_position_config=_position_filter("penetration_only", "penetrative", [position_key]),
location_config=_coworking_location_config(),
hardcore_camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=0,
zoom=7.5,
subject_focus="action",
),
)
_expect_pair(pair, name)
hard_row = pair.get("hardcore_row") or {}
_expect(hard_row.get("action_family") == "penetration", f"{name} action_family should be penetration")
_expect(hard_row.get("position_family") == "penetrative", f"{name} position_family should be penetrative")
_expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
for term in role_terms:
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("viewer" in prompt and "pov" in prompt, f"{name} Krea prompt lost POV wording")
_expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
for term in krea_terms:
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
def smoke_pov_anal_position_routes() -> None:
cases = [
(
"pov_anal_doggy",
"doggy",
("on all fours", "positioned behind her"),
("on all fours directly in front", "penetrates her ass"),
),
(
"pov_anal_bent_over",
"bent_over",
("bent forward", "stands behind her"),
("bent forward at the waist", "penetrates her ass"),
),
(
"pov_anal_face_down",
"face_down_ass_up",
("lies face-down", "ass raised"),
("lying face-down", "penetrates her ass"),
),
(
"pov_anal_standing",
"standing",
("stands braced", "stands behind her"),
("pov standing rear-entry position", "viewer stands behind her"),
),
(
"pov_anal_side_lying",
"side_lying",
("lies on her side", "presses behind her"),
("pov side-lying sex position", "viewer is behind her"),
),
(
"pov_anal_edge_supported",
"edge_supported",
("raised edge", "kneels behind her"),
("pov raised-edge penetration position", "viewer kneels between her legs"),
),
(
"pov_anal_kneeling",
"kneeling",
("kneels forward", "kneels behind her"),
("pov kneeling rear-entry position", "viewer kneels behind her"),
),
]
for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3901):
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=offset,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(
softcore_camera_mode="from_camera_config",
hardcore_camera_mode="from_camera_config",
camera_detail="compact",
),
character_cast=_character_cast(pov_man=True),
hardcore_position_config=_position_filter("anal_only", "anal", [position_key]),
location_config=_coworking_location_config(),
hardcore_camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=0,
zoom=7.5,
subject_focus="action",
),
)
_expect_pair(pair, name)
hard_row = pair.get("hardcore_row") or {}
_expect(hard_row.get("position_family") == "anal", f"{name} position_family should be anal")
_expect(hard_row.get("action_family") == "anal", f"{name} action_family should be anal")
_expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
for term in role_terms:
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("viewer" in prompt and "first-person" in prompt, f"{name} Krea prompt lost POV wording")
_expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
for term in krea_terms:
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
def smoke_double_front_back_route() -> None:
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3911,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(
hardcore_cast="mixed_group",
hardcore_men_count=2,
softcore_camera_mode="from_camera_config",
hardcore_camera_mode="from_camera_config",
camera_detail="compact",
),
character_cast=_character_cast_two_men(),
hardcore_position_config=_anal_double_filter(["front_back"]),
location_config=_coworking_location_config(),
hardcore_camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=0,
zoom=7.5,
subject_focus="action",
),
)
_expect_pair(pair, "double_front_back_route")
hard_row = pair.get("hardcore_row") or {}
_expect(hard_row.get("position_family") == "anal", "double route position_family should be anal")
_expect(hard_row.get("action_family") == "toy_double", "double route action_family should stay toy_double")
_expect("front_back" in (hard_row.get("position_keys") or []), "double route lost front_back key")
role_graph = _expect_text("double_front_back_route.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
_expect("second penetration point from the front" in role_graph, f"double route role graph lost front/back placement: {role_graph}")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text("double_front_back_route.krea_prompt", krea.get("krea_prompt"), 60).lower()
_expect("metadata" in krea.get("method", ""), "double route Krea did not use metadata")
_expect("front-and-back" in prompt, "double route Krea lost front/back position wording")
_expect("second penetration point" in prompt, "double route Krea lost second-contact wording")
_expect("role graph:" not in prompt and "sexual scene:" not in prompt, "double route Krea leaked raw labels")
def smoke_climax_position_routes() -> None:
cases = [
(
"climax_face_down",
"face_down_ass_up",
4001,
_character_cast(),
1,
1,
("lies face-down", "lower back and ass"),
("face-down", "lower back and ass"),
),
(
"climax_side_lying",
"side_lying",
4042,
_character_cast(),
1,
1,
("lies on her side", "thighs and pussy"),
("lies on her side", "thighs and pussy"),
),
(
"climax_lotus_lap",
"lotus_lap",
4001,
_character_cast(),
1,
1,
("sits in man a's lap", "legs wrapped"),
("sits in the man's lap", "legs wrapped"),
),
(
"climax_open_thighs",
"open_thighs",
4001,
_character_cast(),
1,
1,
("lies on her back", "thighs open"),
("lies on her back", "thighs open"),
),
(
"climax_front_back",
"front_back",
4090,
_character_cast_two_men(),
1,
2,
("lies between man a and man b", "man a under her hips"),
("lies between man a and man b", "visible semen lands"),
),
]
for name, position_key, seed, cast, women_count, men_count, role_terms, krea_terms in cases:
row = _prompt_row(
name=name,
category="Hardcore sexual poses",
subcategory="Cumshot and climax",
seed=seed,
character_cast=cast,
women_count=women_count,
men_count=men_count,
hardcore_position_config=_position_filter("climax_only", "climax", [position_key]),
)
_expect_custom_row(row, name)
_expect(row.get("action_family") == "climax", f"{name} action_family should be climax")
_expect(row.get("position_family") == "climax", f"{name} position_family should be climax")
_expect(position_key in (row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
role_graph = _expect_text(f"{name}.source_role_graph", row.get("source_role_graph"), 40).lower()
for term in role_terms:
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("role graph:" not in prompt and "sexual scene:" not in prompt, f"{name} Krea leaked raw labels")
for term in krea_terms:
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
if position_key == "side_lying":
_expect("lower back and ass" not in prompt, f"{name} Krea kept conflicting rear-entry fluid location: {prompt}")
_expect_formatter_outputs(row, name, target="single")
def smoke_interaction_role_graph_routes() -> None:
cases = [
(
"interaction_manual",
"Manual stimulation",
"manual_only",
"manual",
"fingering",
4301,
_character_cast(),
1,
1,
("reclines with thighs open", "fingers visibly stimulating"),
("fingers visibly stimulating", "between her legs"),
),
(
"interaction_clothing_transition",
"Clothing and position transitions",
"interaction_only",
"interaction",
"position_transition",
4302,
_character_cast(),
1,
1,
("mid-transition", "moving clothing aside"),
("mid-transition", "guiding the woman's hips"),
),
(
"interaction_body_worship",
"Body worship and touching",
"interaction_only",
"interaction",
"body_worship",
4301,
_character_cast(),
1,
1,
("kisses down her body", "hands tracing"),
("kisses down her body", "hands tracing"),
),
(
"interaction_camera_performance",
"Camera performance",
"interaction_only",
"interaction",
"camera_showing",
4301,
_character_cast(),
1,
1,
("faces the camera", "creator-shot reveal"),
("faces the camera", "creator-shot reveal"),
),
(
"interaction_aftercare",
"Aftercare and cleanup",
"interaction_only",
"interaction",
"aftercare",
4301,
_character_cast(),
1,
1,
("lie close together after sex", "post-sex cuddle"),
("lie close together after sex", "post-sex cuddle"),
),
(
"interaction_group_coordination",
"Group coordination",
"interaction_only",
"interaction",
"watching",
4301,
_character_cast_two_men(),
1,
2,
("is centered", "hold and present"),
("is centered", "each role clearly visible"),
),
]
for name, subcategory, focus, family, position_key, seed, cast, women_count, men_count, role_terms, krea_terms in cases:
row = _prompt_row(
name=name,
category="Hardcore sexual poses",
subcategory=subcategory,
seed=seed,
character_cast=cast,
women_count=women_count,
men_count=men_count,
hardcore_position_config=_position_filter(focus, family, [position_key]),
)
_expect_custom_row(row, name)
expected_action_family = "manual" if family == "manual" else "foreplay"
_expect(
row.get("action_family") == expected_action_family,
f"{name} action_family mismatch: {row.get('action_family')} != {expected_action_family}",
)
_expect(row.get("position_family") == family, f"{name} position_family mismatch: {row.get('position_family')}")
_expect(position_key in (row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
role_graph = _expect_text(f"{name}.source_role_graph", row.get("source_role_graph"), 40).lower()
for term in role_terms:
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("role graph:" not in prompt and "sexual scene:" not in prompt, f"{name} Krea leaked raw labels")
for term in krea_terms:
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
_expect_formatter_outputs(row, name, target="single")
def smoke_fallback_role_graph_routes() -> None:
cases = [
(
"fallback_solo_woman_manual",
("woman",),
"Manual stimulation",
"manual_only",
"manual",
"fingering",
4401,
1,
0,
("reclines with thighs open", "one hand between her legs"),
("one hand between her legs", "fingers visibly stimulating"),
),
(
"fallback_solo_man_climax",
("man",),
"Cumshot and climax",
"climax_only",
"climax",
"open_thighs",
4401,
0,
1,
("solo visible ejaculation pose", "semen visible"),
("solo visible ejaculation pose", "visible semen on skin"),
),
(
"fallback_women_only_oral",
("woman", "woman"),
"Oral sex",
"oral_only",
"oral",
"reclining_oral",
4401,
2,
0,
("woman a kneels between woman b", "uses tongue and fingers"),
("woman a kneels between woman b", "uses tongue and fingers"),
),
(
"fallback_women_only_threesome",
("woman", "woman", "woman"),
"Threesomes",
"threesome_only",
"threesome",
"front_back",
4401,
3,
0,
("uses a strap-on", "gives oral contact"),
("uses a strap-on", "gives oral contact"),
),
(
"fallback_men_only_oral",
("man", "man"),
"Oral sex",
"oral_only",
"oral",
"kneeling",
4401,
0,
2,
("man a kneels", "takes man b's penis"),
("man a kneels", "takes man b's penis"),
),
(
"fallback_men_only_threesome",
("man", "man", "man"),
"Threesomes",
"threesome_only",
"threesome",
"front_back",
4401,
0,
3,
("penetrates man b anally", "gives oral contact"),
("penetrates man b anally", "gives oral contact"),
),
(
"fallback_mixed_threesome",
("woman", "man", "man"),
"Threesomes",
"threesome_only",
"threesome",
"front_back",
4401,
1,
2,
("thrusts his penis into woman a", "uses mouth and hands"),
("thrusts his penis into woman a", "uses mouth and hands"),
),
]
for name, subjects, subcategory, focus, family, position_key, seed, women_count, men_count, role_terms, krea_terms in cases:
row = _prompt_row(
name=name,
category="Hardcore sexual poses",
subcategory=subcategory,
seed=seed,
character_cast=_character_cast_subjects(subjects),
women_count=women_count,
men_count=men_count,
hardcore_position_config=_position_filter(focus, family, [position_key]),
)
_expect_custom_row(row, name)
_expect(row.get("position_family") == family, f"{name} position_family mismatch: {row.get('position_family')}")
if family == "threesome":
_expect(row.get("action_family") == "threesome", f"{name} action_family should be threesome")
_expect(position_key in (row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
role_graph = _expect_text(f"{name}.source_role_graph", row.get("source_role_graph"), 30).lower()
for term in role_terms:
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("role graph:" not in prompt and "sexual scene:" not in prompt, f"{name} Krea leaked raw labels")
for term in krea_terms:
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
if name == "fallback_solo_man_climax":
_expect("lower back and ass" not in prompt, f"{name} Krea kept conflicting solo male climax detail: {prompt}")
_expect_formatter_outputs(row, name, target="single")
def smoke_no_expression_fallback() -> None:
cast = pb.build_character_slot_json(
subject_type="woman",
label="A",
age="25-year-old adult",
ethnicity="western_european",
body="slim",
descriptor_detail="full",
expression_enabled=False,
)["character_cast"]
row = _prompt_row(
name="hardcore_expression_disabled",
category="Hardcore sexual poses",
subcategory="Penetrative sex",
seed=2301,
character_cast=cast,
women_count=1,
men_count=1,
hardcore_position_config=_action_filter("penetration_only"),
)
_expect_custom_row(row, "hardcore_expression_disabled")
_expect(not row.get("expression"), "expression should stay disabled without fallback")
_expect("Facial expressions:" not in row.get("prompt", ""), "disabled expression leaked into prompt")
_expect_formatter_outputs(row, "hardcore_expression_disabled", target="single")
def smoke_formatter_metadata_fixtures() -> None:
cases = [
{
"name": "fixture_penetration_text_noise",
"row": _fixture_hardcore_row(),
"krea_terms": ("penis thrusts",),
"sdxl_terms": ("penetrative sex", "missionary"),
"caption_terms": ("penetrative action",),
},
{
"name": "fixture_manual_source_family",
"row": _fixture_hardcore_row(
subcategory="Manual stimulation",
subcategory_slug="manual_stimulation",
item="wet fingers moving between the thighs, one hand braced on the hip, wet shine on fingers and inner thighs",
custom_item="Manual stimulation",
item_axis_values={
"position": "kneeling hand-between-thighs position",
"manual_act": "wet fingers moving between the thighs",
},
composition="close crop on hands and face",
source_composition="close crop on hands and face",
role_graph=(
"Woman A reclines with thighs open while Man A's hand is between her legs, "
"fingers visibly stimulating her pussy."
),
source_role_graph=(
"Woman A reclines with thighs open while Man A's hand is between her legs, "
"fingers visibly stimulating her pussy."
),
action_family="manual",
position_family="manual",
position_key="fingering",
position_keys=["fingering", "open_thighs"],
),
"krea_terms": ("fingers visibly stimulating",),
"sdxl_terms": ("manual stimulation", "fingering"),
"caption_terms": ("manual action",),
},
{
"name": "fixture_climax_family",
"row": _fixture_hardcore_row(
subcategory="Cumshot and climax",
subcategory_slug="cumshot_climax",
item="external cumshot with cum on lower back and ass, explicit semen aftermath visible",
custom_item="Cumshot and climax",
item_axis_values={"position": "on all fours with hips raised"},
composition="low-angle post-orgasm frame",
source_composition="low-angle post-orgasm frame",
role_graph=(
"Woman A is on all fours with hips raised while Man A is positioned behind her "
"and ejaculates semen across her ass, thighs, and lower back."
),
source_role_graph=(
"Woman A is on all fours with hips raised while Man A is positioned behind her "
"and ejaculates semen across her ass, thighs, and lower back."
),
action_family="climax",
position_family="climax",
position_key="doggy",
position_keys=["doggy"],
),
"krea_terms": ("ejaculates semen",),
"sdxl_terms": ("climax", "semen"),
"caption_terms": ("climax action",),
},
]
for case in cases:
name = case["name"]
row = case["row"]
_expect_custom_row(row, name)
metadata = _json(row)
krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target="single")
krea_prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 40).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("role graph:" not in krea_prompt and "sexual pose:" not in krea_prompt, f"{name}.krea leaked raw labels")
for term in case["krea_terms"]:
_expect(term in krea_prompt, f"{name}.krea missing {term!r}")
sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=metadata, target="single", trigger=SdxlTrigger, prepend_trigger=True)
sdxl_prompt = _expect_text(f"{name}.sdxl_prompt", sdxl.get("sdxl_prompt"), 40).lower()
_expect("metadata" in sdxl.get("method", ""), f"{name}.sdxl did not use metadata")
for term in case["sdxl_terms"]:
_expect(term in sdxl_prompt, f"{name}.sdxl missing {term!r}")
caption, method = caption_naturalizer.naturalize_caption("", metadata_json=metadata, trigger=Trigger, include_trigger=True)
caption_text = _expect_text(f"{name}.caption", caption, 40).lower()
_expect("metadata" in method, f"{name}.caption did not use metadata")
for term in case["caption_terms"]:
_expect(term in caption_text, f"{name}.caption missing {term!r}")
route_row = _fixture_hardcore_row(
formatter_hints={
"all": ["shared route anchor"],
"krea": ["krea readable anchor"],
"sdxl": ["sdxl route tag"],
"caption": ["caption route phrase"],
}
)
_expect_custom_row(route_row, "fixture_formatter_hints")
metadata = _json(route_row)
krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target="single")
krea_prompt = _expect_text("fixture_formatter_hints.krea_prompt", krea.get("krea_prompt"), 40).lower()
_expect("shared route anchor" in krea_prompt, "Krea formatter missed shared formatter hint")
_expect("krea readable anchor" in krea_prompt, "Krea formatter missed Krea formatter hint")
_expect("sdxl route tag" not in krea_prompt, "Krea formatter leaked SDXL formatter hint")
_expect("caption route phrase" not in krea_prompt, "Krea formatter leaked caption formatter hint")
sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=metadata, target="single", trigger=SdxlTrigger, prepend_trigger=True)
sdxl_prompt = _expect_text("fixture_formatter_hints.sdxl_prompt", sdxl.get("sdxl_prompt"), 40).lower()
_expect("shared route anchor" in sdxl_prompt, "SDXL formatter missed shared formatter hint")
_expect("sdxl route tag" in sdxl_prompt, "SDXL formatter missed SDXL formatter hint")
_expect("krea readable anchor" not in sdxl_prompt, "SDXL formatter leaked Krea formatter hint")
_expect("caption route phrase" not in sdxl_prompt, "SDXL formatter leaked caption formatter hint")
caption, method = caption_naturalizer.naturalize_caption("", metadata_json=metadata, trigger=Trigger, include_trigger=True)
caption_text = _expect_text("fixture_formatter_hints.caption", caption, 40).lower()
_expect("metadata" in method, "Caption formatter hints fixture did not use metadata")
_expect("shared route anchor" in caption_text, "Caption naturalizer missed shared formatter hint")
_expect("caption route phrase" in caption_text, "Caption naturalizer missed caption formatter hint")
_expect("krea readable anchor" not in caption_text, "Caption naturalizer leaked Krea formatter hint")
_expect("sdxl route tag" not in caption_text, "Caption naturalizer leaked SDXL formatter hint")
external_pair = {
"trigger": Trigger,
"shared_descriptor": "25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes",
"shared_cast_descriptors": [
"Woman A: 25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes",
"Man A: 40-year-old adult man, average figure, tan skin, dark hair",
],
"options": {
"softcore_cast": "same_as_hardcore",
"continuity": "same_creator_same_room",
"softcore_level": "lingerie_tease",
"hardcore_level": "hardcore",
},
"softcore_row": {
"prompt": f"{Trigger}, embedded soft pair prompt.",
"caption": f"{Trigger}, embedded soft pair caption.",
"negative_prompt": "bad hands, bad hands",
"subject_type": "configured_cast",
"primary_subject": "woman",
"item": "red satin lingerie set",
"softcore_item_prompt_label": "Erotic outfit",
"pose": "standing together beside a mirror",
"scene_text": "private studio room with warm light",
"composition": "mirror teaser composition",
"expression": "confident soft smile",
"softcore_partner_styling": {
"outfits": ["Man A wears a black buttoned shirt and dark trousers"],
"pose": "standing close with one hand at the waist",
},
"camera_config": {
"camera_mode": "standard",
"custom_camera_prompt": "front-right quarter view, eye-level shot, medium shot",
},
"camera_directive": "Camera: row soft front-right view.",
"camera_scene_directive": "Row soft mirror camera layout.",
},
"hardcore_row": {
"prompt": f"{Trigger}, embedded hard pair prompt.",
"caption": f"{Trigger}, embedded hard pair caption.",
"negative_prompt": "low quality, low quality",
"subject_type": "configured_cast",
"primary_subject": "configured_cast",
"item": "missionary position while full-body penetrative sex",
"custom_item": "Penetrative sex",
"item_label": "Sexual pose",
"item_axis_values": {
"position": "missionary position",
"penetration_act": "full-body penetrative sex",
},
"scene_text": "private studio room with warm light",
"composition": "front-facing full-body frame",
"source_composition": "front-facing full-body frame",
"role_graph": (
"Woman A lies on her back with legs open around Man A's hips while Man A is above her "
"between her thighs; Man A's penis thrusts into her pussy."
),
"source_role_graph": (
"Woman A lies on her back with legs open around Man A's hips while Man A is above her "
"between her thighs; Man A's penis thrusts into her pussy."
),
"expression": "focused adult expression",
"action_family": "penetration",
"position_family": "penetrative",
"position_key": "missionary",
"position_keys": ["missionary"],
"hardcore_clothing_state": "Woman A's red satin lingerie set is pushed aside at the hips",
"camera_config": {
"camera_mode": "standard",
"custom_camera_prompt": "right side view, eye-level shot, medium shot",
},
"camera_directive": "Camera: row hard right-side view.",
"camera_scene_directive": "Row hard body-aligned camera layout.",
},
}
external_metadata = _json(external_pair)
krea_pair = krea_formatter.format_krea2_prompt("", metadata_json=external_metadata, target="auto")
krea_soft = _expect_text("fixture_external_pair.krea_soft", krea_pair.get("krea_softcore_prompt"), 40).lower()
krea_hard = _expect_text("fixture_external_pair.krea_hard", krea_pair.get("krea_hardcore_prompt"), 40).lower()
_expect(krea_pair.get("method") == "metadata_json:krea2(insta_of_pair)", "External pair Krea route changed method")
_expect("black buttoned shirt" in krea_soft, "External pair Krea soft route lost embedded partner styling")
_expect_no_softcore_noise("fixture_external_pair.krea_soft", krea_pair.get("krea_softcore_prompt"))
_expect("styled creator-teaser frame" in krea_soft, "External pair Krea soft route lost clean cast-presence wording")
_expect("red satin lingerie set" in krea_hard, "External pair Krea hard route lost embedded clothing state")
_expect("row hard right-side view" in krea_hard, "External pair Krea hard route lost embedded camera directive")
_expect_no_duplicate_comma_items("fixture_external_pair.krea_negative", krea_pair.get("negative_prompt"))
sdxl_pair = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=external_metadata,
target="auto",
trigger=SdxlTrigger,
prepend_trigger=True,
)
sdxl_soft = _expect_text("fixture_external_pair.sdxl_soft", sdxl_pair.get("sdxl_softcore_prompt"), 40).lower()
sdxl_hard = _expect_text("fixture_external_pair.sdxl_hard", sdxl_pair.get("sdxl_hardcore_prompt"), 40).lower()
_expect(sdxl_pair.get("method") == "metadata_json:sdxl(insta_of_pair)", "External pair SDXL route changed method")
_expect_trigger_once("fixture_external_pair.sdxl_soft", sdxl_pair.get("sdxl_softcore_prompt"), SdxlTrigger)
_expect_trigger_once("fixture_external_pair.sdxl_hard", sdxl_pair.get("sdxl_hardcore_prompt"), SdxlTrigger)
_expect("black buttoned shirt" in sdxl_soft, "External pair SDXL soft route lost embedded partner styling")
_expect("1man" in sdxl_soft, "External pair SDXL soft route lost partner count tag")
_expect("40-year-old adult man" in sdxl_soft, "External pair SDXL soft route lost partner descriptor")
_expect("softcore teaser" in sdxl_soft, "External pair SDXL soft route lost softcore teaser tag")
_expect_no_softcore_noise("fixture_external_pair.sdxl_soft", sdxl_pair.get("sdxl_softcore_prompt"))
_expect("red satin lingerie set" in sdxl_hard, "External pair SDXL hard route lost embedded clothing state")
_expect("row hard right-side view" in sdxl_hard, "External pair SDXL hard route lost embedded camera directive")
_expect_no_duplicate_comma_items("fixture_external_pair.sdxl_negative", sdxl_pair.get("negative_prompt"))
caption, method = caption_naturalizer.naturalize_caption(
"",
metadata_json=external_metadata,
target="auto",
trigger=Trigger,
include_trigger=True,
)
caption_text = _expect_text("fixture_external_pair.caption", caption, 40).lower()
_expect(method == "metadata_json:metadata(insta_of_pair)", "External pair caption route changed method")
_expect_trigger_once("fixture_external_pair.caption", caption, Trigger)
_expect("black buttoned shirt" in caption_text, "External pair caption route lost embedded partner styling")
_expect("softcore side" in caption_text, "External pair caption route lost softcore side")
_expect("hardcore side" in caption_text, "External pair caption route lost hardcore side")
_expect("softcore version" not in caption_text and "hardcore version" not in caption_text, "External pair caption kept version labels")
generated = _prompt_row(
name="fixture_generated_formatter_invariants",
category="Casual clothes",
subcategory="Casual clothes / Smart casual",
seed=4101,
men_count=0,
character_cast=_character_cast_subjects(["woman"]),
camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
location_config=_coworking_location_config(),
)
_expect_row_base(generated, "fixture_generated_formatter_invariants")
_expect(generated.get("source") == "json_category", "Generated formatter invariant should use category JSON route")
generated_metadata = _json(generated)
generated_item = _expect_text("fixture_generated_formatter_invariants.item", generated.get("item"), 8)
item_anchor = " ".join(generated_item.lower().split()[:3])
krea = krea_formatter.format_krea2_prompt("", metadata_json=generated_metadata, target="single")
krea_prompt = _expect_text("fixture_generated_formatter_invariants.krea_prompt", krea.get("krea_prompt"), 40).lower()
_expect("metadata" in krea.get("method", ""), "Generated Krea invariant did not use metadata")
_expect(item_anchor in krea_prompt, "Generated Krea prompt lost clothing/item metadata")
_expect("coworking lounge" in krea_prompt, "Generated Krea prompt lost scene metadata")
_expect("front-right quarter view" in krea_prompt, "Generated Krea prompt lost camera metadata")
sdxl = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=generated_metadata,
target="single",
trigger=SdxlTrigger,
prepend_trigger=True,
)
sdxl_prompt = _expect_text("fixture_generated_formatter_invariants.sdxl_prompt", sdxl.get("sdxl_prompt"), 40).lower()
_expect("metadata" in sdxl.get("method", ""), "Generated SDXL invariant did not use metadata")
_expect_trigger_once("fixture_generated_formatter_invariants.sdxl_prompt", sdxl.get("sdxl_prompt"), SdxlTrigger)
_expect(item_anchor in sdxl_prompt, "Generated SDXL prompt lost clothing/item metadata")
_expect("coworking lounge" in sdxl_prompt, "Generated SDXL prompt lost scene metadata")
_expect("front-right quarter view" in sdxl_prompt, "Generated SDXL prompt lost camera metadata")
for leaked_label in ("camera:", "composition:", "scene:", "setting:", "pose:", "clothing:"):
_expect(leaked_label not in sdxl_prompt, f"Generated SDXL prompt leaked raw label {leaked_label!r}")
caption, method = caption_naturalizer.naturalize_caption(
"",
metadata_json=generated_metadata,
trigger=Trigger,
include_trigger=False,
caption_profile="training_dense",
)
caption_text = _expect_text("fixture_generated_formatter_invariants.caption", caption, 40).lower()
_expect("metadata" in method, "Generated caption invariant did not use metadata")
_expect_trigger_once("fixture_generated_formatter_invariants.caption", caption, Trigger)
_expect(item_anchor in caption_text, "Generated training caption lost clothing/item metadata")
_expect("coworking lounge" in caption_text, "Generated training caption lost scene metadata")
def _expect_comfy_input_spec(node_name: str, group: str, input_name: str, spec: Any) -> None:
_expect(isinstance(input_name, str) and input_name, f"{node_name}.{group} has invalid input name {input_name!r}")
if group == "hidden":
_expect(
isinstance(spec, (str, tuple)),
f"{node_name}.{input_name} hidden input should be a ComfyUI hidden token or tuple",
)
return
_expect(isinstance(spec, tuple) and spec, f"{node_name}.{input_name} visible input has invalid spec")
type_spec = spec[0]
_expect(
isinstance(type_spec, str) or isinstance(type_spec, list),
f"{node_name}.{input_name} input type should be a socket type or choices list",
)
if isinstance(type_spec, list):
_expect(type_spec, f"{node_name}.{input_name} choice list should not be empty")
_expect(len(spec) >= 2 and isinstance(spec[1], dict), f"{node_name}.{input_name} visible input options should be a dict")
_expect("tooltip" in spec[1], f"{node_name}.{input_name} visible input is missing tooltip")
def smoke_node_runtime_contracts() -> None:
node_names = sorted(sxcp_nodes.NODE_CLASS_MAPPINGS)
_expect(node_names, "Node registry is empty")
_expect(
set(sxcp_nodes.NODE_CLASS_MAPPINGS) == set(sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS),
"Node class and display registries are out of sync",
)
_expect(len(node_names) >= 50, "Node registry unexpectedly small")
for node_name in node_names:
node_class = sxcp_nodes.NODE_CLASS_MAPPINGS[node_name]
display_name = sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS.get(node_name)
_expect(isinstance(display_name, str) and display_name, f"{node_name} has no display name")
input_types = node_class.INPUT_TYPES()
_expect(isinstance(input_types, dict), f"{node_name}.INPUT_TYPES should return a dict")
_expect("required" in input_types, f"{node_name}.INPUT_TYPES lost required group")
for group, inputs in input_types.items():
_expect(group in {"required", "optional", "hidden"}, f"{node_name}.INPUT_TYPES has unknown group {group!r}")
_expect(isinstance(inputs, dict), f"{node_name}.{group} inputs should be a dict")
for input_name, spec in inputs.items():
_expect_comfy_input_spec(node_name, group, input_name, spec)
return_types = getattr(node_class, "RETURN_TYPES", None)
_expect(isinstance(return_types, tuple) and return_types, f"{node_name}.RETURN_TYPES should be a non-empty tuple")
for output_type in return_types:
_expect(isinstance(output_type, str) and output_type, f"{node_name}.RETURN_TYPES contains an invalid output type")
return_names = getattr(node_class, "RETURN_NAMES", None)
if return_names is not None:
_expect(isinstance(return_names, tuple), f"{node_name}.RETURN_NAMES should be a tuple when present")
_expect(len(return_names) == len(return_types), f"{node_name}.RETURN_NAMES length does not match RETURN_TYPES")
for output_name in return_names:
_expect(isinstance(output_name, str) and output_name, f"{node_name}.RETURN_NAMES contains an invalid name")
function_name = getattr(node_class, "FUNCTION", None)
_expect(isinstance(function_name, str) and function_name, f"{node_name}.FUNCTION should name the executor method")
_expect(callable(getattr(node_class(), function_name, None)), f"{node_name}.FUNCTION target is not callable")
category = getattr(node_class, "CATEGORY", None)
_expect(isinstance(category, str) and category, f"{node_name}.CATEGORY should be a non-empty string")
def smoke_node_utility_registration() -> None:
required_nodes = [
"SxCPGlobalSeed",
"SxCPSeedControl",
"SxCPSeedLocker",
"SxCPSDXLBucketSize",
"SxCPKrea2ResolutionSelector",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
global_seed_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPGlobalSeed"].INPUT_TYPES().get("required") or {}
global_seed_tooltip = global_seed_inputs.get("global_seed", (None, {}))[1].get("tooltip", "")
_expect("Connect seed to generator seed" in global_seed_tooltip, "Global Seed tooltip should explain generator wiring")
seed_control = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSeedControl"]
seed_inputs = seed_control.INPUT_TYPES().get("required") or {}
_expect("category_seed_mode" in seed_inputs, "Seed Control lost category seed mode input")
_expect("tooltip" in seed_inputs["category_seed_mode"][1], "Seed Control tooltip injection missing")
_expect(seed_control.RETURN_NAMES == ("seed_config", "summary"), "Seed Control lost visible summary output")
category_seed_tooltip = node_tooltips._tooltip_for_input("SxCPSeedControl", "category_seed_mode")
_expect(
"random rerolls this axis at queue time" in category_seed_tooltip
and "field value stays unchanged" in category_seed_tooltip,
"Node tooltip policy lost Seed Control override",
)
_expect(
"Autoscaling switch input" in node_tooltips._tooltip_for_input("SxCPIndexSwitch", "input_12"),
"Node tooltip policy lost autoscaling input fallback",
)
seed_control_config, seed_control_summary = seed_control().build(
"fixed",
-1,
"follow_main",
-1,
"random",
-1,
"auto",
123,
"auto",
-1,
"fixed",
777,
"follow_main",
888,
"auto",
-1,
"fixed",
999,
)
parsed_seed_control = json.loads(seed_control_config)
_expect(parsed_seed_control.get("category_seed") == 0, "Seed Control fixed mode did not clamp negative seed")
_expect(parsed_seed_control.get("subcategory_seed") == -1, "Seed Control follow_main mode should emit -1")
_expect(int(parsed_seed_control.get("content_seed", -1)) >= 0, "Seed Control random mode did not emit resolved seed")
_expect(parsed_seed_control.get("person_seed") == 123, "Seed Control auto mode did not preserve explicit value")
_expect(parsed_seed_control.get("pose_seed") == 777, "Seed Control fixed mode did not preserve positive seed")
_expect(parsed_seed_control.get("role_seed") == -1, "Seed Control follow_main role did not emit -1")
_expect(parsed_seed_control.get("composition_seed") == 999, "Seed Control fixed composition seed changed")
_expect("category=0" in seed_control_summary, "Seed Control summary lost fixed resolved seed")
_expect("subcategory=follow_main" in seed_control_summary, "Seed Control summary lost follow_main marker")
_expect(
f"content={parsed_seed_control['content_seed']}" in seed_control_summary,
"Seed Control summary lost random resolved seed value",
)
seed_locker = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSeedLocker"]
locker_inputs = seed_locker.INPUT_TYPES().get("required") or {}
_expect(
list(locker_inputs["reroll_axis"][0]) == seed_config.seed_reroll_axis_choices(),
"Seed Locker reroll choices drifted from seed_config",
)
seed, global_seed_config, summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPGlobalSeed"]().build(12345)
parsed_seed = json.loads(global_seed_config)
_expect(seed == 12345, "Global Seed did not return the clamped seed")
_expect(parsed_seed, "Global Seed config should not be empty")
_expect(all(int(value) == 12345 for value in parsed_seed.values()), "Global Seed config did not lock every axis")
_expect("all axes locked" in summary, "Global Seed summary changed unexpectedly")
locker_config, locker_summary = seed_locker().build(12345, "pose", 999)
parsed_locker = json.loads(locker_config)
_expect(parsed_locker.get("pose_seed") == 999, "Seed Locker did not apply pose reroll seed")
_expect("reroll pose" in locker_summary, "Seed Locker summary lost reroll axis")
bucket_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLBucketSize"]()
bucket_a = bucket_node.build("portrait", 77, 3, 0)
bucket_b = bucket_node.build("portrait", 77, 3, 0)
_expect(bucket_a == bucket_b, "SDXL bucket should be deterministic for fixed seed and row")
_expect(bucket_a[3] == "portrait", "SDXL bucket ignored orientation filter")
bucket_seed_config = json.dumps({"camera_seed": 9001})
bucket_seeded_a = bucket_node.build("landscape", -1, 4, 0, bucket_seed_config)
bucket_seeded_b = bucket_node.build("landscape", -1, 4, 0, bucket_seed_config)
_expect(bucket_seeded_a == bucket_seeded_b, "SDXL bucket should honor seed_config aliases deterministically")
_expect(
bucket_node._configured_bucket_seed({"camera_seed": "42"}) == 42,
"SDXL bucket seed should delegate through composition/camera seed aliases",
)
krea_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2ResolutionSelector"]()
krea_width, krea_height, _resolution, aspect_ratio, api_aspect, _api_resolution, *_rest = krea_node.select("1.0MP", "9:16")
krea_config = json.loads(_rest[-1])
_expect(krea_height > krea_width, "Krea2 9:16 selector should return portrait dimensions")
_expect(aspect_ratio == "9:16", "Krea2 selector lost requested aspect ratio")
_expect(api_aspect == "9:16", "Krea2 selector lost API aspect mapping")
_expect(krea_config.get("width") == krea_width and krea_config.get("height") == krea_height, "Krea2 config_json dimensions mismatch")
def smoke_server_route_payload_policy() -> None:
requested, selected, available = index_switch_policy.input_selection(
0,
"zero_based",
"fallback",
{"input_1": "first"},
)
_expect((requested, selected, available) == (1, 1, [1]), "Index switch policy zero-based selection changed")
_expect(
index_switch_policy.route_selection(65, "one_based", "wrap") == (65, 1),
"Index switch policy wrap routing changed",
)
_expect(
index_switch_policy.lazy_inputs(2, "pick_input", "one_based", "fallback", {"input_2": "second"}) == ["input_2"],
"Index switch policy lazy input selection changed",
)
switch = loop_nodes.SxCPIndexSwitch()
picked = switch.switch(
2,
"pick_input",
"one_based",
"fallback",
input_1="first",
input_2="second",
fallback="fallback",
)
_expect(picked[0] == "second", "Index Switch pick_input did not select the requested input")
_expect(picked[1] == 2, "Index Switch pick_input selected_index changed")
_expect("selected=input_2" in picked[2], "Index Switch pick_input status lost selected input")
routed = switch.switch(3, "route_output", "one_based", "fallback", route_value="routed")
_expect(routed[0] == "routed", "Index Switch route_output primary value changed")
_expect(routed[1] == 3, "Index Switch route_output selected_index changed")
_expect(routed[5] == "routed", "Index Switch route_output did not route value to output_3")
key = "smoke_route_payload"
loop_nodes._ACCUMULATOR_STORES[key] = [
{"id": "first", "value": "alpha", "_sxcp_preview_key": "first-key"},
{"id": "second", "value": "beta", "_sxcp_preview_key": "second-key"},
]
try:
listed = server_routes.accumulator_list_payload({"store_key": key, "preview_limit": "0"})
_expect(listed.get("count") == 2, "Accumulator list payload lost stored entries")
_expect(listed["entries"][0].get("value") == "alpha", "Accumulator list payload lost value summary")
moved = server_routes.accumulator_move_payload({"store_key": key, "entry_id": "second", "target_index": "1"})
_expect(moved.get("moved") is True, "Accumulator move payload did not report movement")
_expect(moved.get("from_index") == 2 and moved.get("to_index") == 1, "Accumulator move payload changed indices")
_expect(moved["entries"][0].get("id") == "second", "Accumulator move payload did not reorder entries")
deleted = server_routes.accumulator_delete_payload({"store_key": key, "preview_key": "first-key"})
_expect(deleted.get("removed") == 1, "Accumulator delete payload did not remove by preview key")
_expect(deleted.get("count") == 1, "Accumulator delete payload count changed")
cleared = server_routes.accumulator_delete_payload({"store_key": key, "clear": True})
_expect(cleared.get("removed") == 1 and cleared.get("count") == 0, "Accumulator clear payload changed")
finally:
loop_nodes._ACCUMULATOR_STORES.pop(key, None)
with tempfile.TemporaryDirectory() as tmpdir:
previous_profile_dir = character_profile.PROFILE_DIR
character_profile.PROFILE_DIR = Path(tmpdir)
try:
profile = character_profile.build_character_profile_json(
profile_name="route source",
source="manual",
subject_type="woman",
age="28-year-old adult",
body="slim",
hair="long black hair",
save_now=False,
)
saved = server_routes.profile_save_cached_payload(
{"profile_name": "Route Save!*", "profile_json": profile["profile_json"]}
)
saved_path = Path(saved.get("saved_path") or "")
_expect(saved.get("status") == "saved", "Profile save payload did not save")
_expect(saved.get("profile_name") == "Route_Save", "Profile save payload did not sanitize requested name")
_expect(saved_path.exists(), "Profile save payload did not write profile file")
finally:
character_profile.PROFILE_DIR = previous_profile_dir
def smoke_seed_config_policy() -> None:
_expect(pb.SEED_AXIS_SALTS is seed_config.SEED_AXIS_SALTS, "prompt_builder seed salts should delegate to seed_config")
_expect(pb.seed_mode_choices() == seed_config.seed_mode_choices(), "seed mode choices drifted from seed_config")
_expect(
pb.seed_reroll_axis_choices() == seed_config.seed_reroll_axis_choices(),
"seed reroll axis choices drifted from seed_config",
)
_expect(pb.normalize_seed_mode("follow main") == "follow_main", "seed mode normalizer should accept spaced labels")
_expect(pb.normalize_seed_mode("FOLLOW-MAIN") == "follow_main", "seed mode normalizer should accept hyphenated labels")
_expect(pb.normalize_reroll_axis("content pose") == "content_pose", "reroll axis normalizer should accept spaced labels")
fixed_config = json.loads(
pb.build_seed_config_json(
category_seed=-1,
content_seed=123,
pose_seed=456,
role_seed=789,
category_seed_mode="fixed",
content_seed_mode="fixed",
pose_seed_mode="follow-main",
role_seed_mode="auto",
)
)
_expect(fixed_config["category_seed"] == 0, "fixed seed mode should clamp negative seeds to zero")
_expect(fixed_config["content_seed"] == 123, "fixed seed mode should preserve positive seed")
_expect(fixed_config["pose_seed"] == -1, "follow_main seed mode should emit unlocked axis")
_expect(fixed_config["role_seed"] == 789, "auto seed mode should preserve numeric seed")
parsed = pb._parse_seed_config({"item_seed": "44", "pose_seed": "55", "bad": "nope"})
_expect(parsed == {"item_seed": 44, "pose_seed": 55}, "seed parser should keep integer-like values only")
_expect(pb._configured_axis_seed(parsed, "content") == 44, "content axis should honor item_seed alias")
_expect(pb._configured_axis_seed(parsed, "role") == 55, "role axis should honor pose seed alias")
_expect(
seed_config.configured_seed_from_axes({"camera_seed": "88", "content_seed": "99"}, ("composition", "content")) == 88,
"seed helper should honor axis aliases in precedence order",
)
_expect(
seed_config.configured_seed_from_axes('{"bad": "json"', ("content",)) is None,
"seed helper should return no seed for invalid config JSON",
)
locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="content pose", reroll_seed=999))
_expect(locked["content_seed"] == 999, "content_pose reroll should alter content seed")
_expect(locked["pose_seed"] == 999 and locked["role_seed"] == 999, "content_pose reroll should alter pose and role seeds")
_expect(locked["scene_seed"] == 100, "content_pose reroll should leave scene locked")
axis_trace = seed_config.axis_seed_trace({"content_seed": 44}, 99, 3, axes=("content", "scene"))
_expect(axis_trace["content"]["source"] == "configured", "Seed axis trace lost configured source")
_expect(axis_trace["content"]["seed"] == 44, "Seed axis trace lost configured seed")
_expect(axis_trace["scene"]["source"] == "main", "Seed axis trace lost main source")
_expect(axis_trace["scene"]["seed"] == 99, "Seed axis trace lost main seed")
_expect(
axis_trace["content"]["rng_seed"] == seed_config.row_seed(44, 3, seed_config.SEED_AXIS_SALTS["content"]),
"Seed axis trace lost content RNG seed",
)
rng_a = pb._axis_rng({"content_seed": 123}, "content", 999, 7)
rng_b = seed_config.axis_rng({"content_seed": 123}, "content", 999, 7)
_expect(rng_a.random() == rng_b.random(), "prompt_builder axis RNG should delegate to seed_config")
_expect(pb._row_seed(123, 7, 41) == seed_config.row_seed(123, 7, 41), "row seed wrapper drifted from seed_config")
deterministic_seed = 3901
deterministic_config = pb.build_seed_lock_config_json(base_seed=deterministic_seed)
deterministic_kwargs = {
"name": "seed_config_policy_deterministic",
"category": "Hardcore sexual poses",
"subcategory": "Penetrative sex",
"seed": deterministic_seed,
"seed_config": deterministic_config,
"character_cast": _character_cast(),
"hardcore_position_config": _action_filter("penetration_only"),
"location_config": _coworking_location_config(),
}
deterministic_a = _prompt_row(**deterministic_kwargs)
deterministic_b = _prompt_row(**deterministic_kwargs)
_expect(deterministic_a == deterministic_b, "locked seed config should reproduce identical prompt row output")
slot_descriptor_keys = ("age_band", "body_phrase", "skin", "hair", "eyes", "figure")
def slot_seed_cast(slot_seed: int) -> str:
return pb.build_character_slot_json(
subject_type="woman",
label="A",
slot_seed=slot_seed,
age="random",
ethnicity="random",
figure="random",
body="random",
hair_color="random",
hair_length="random",
hair_style="random",
descriptor_detail="full",
)["character_cast"]
def slot_seed_row(character_cast: str, reroll_seed: int) -> dict[str, Any]:
return _prompt_row(
name=f"seed_config_policy_slot_seed_{reroll_seed}",
category="Casual clothes",
subcategory="Casual clothes / Streetwear",
seed=41001,
seed_config=pb.build_seed_lock_config_json(
base_seed=41001,
reroll_axis="person",
reroll_seed=reroll_seed,
),
character_cast=character_cast,
women_count=1,
men_count=0,
)
seeded_slot_cast = slot_seed_cast(50123)
seeded_slot_a = slot_seed_row(seeded_slot_cast, 60001)
seeded_slot_b = slot_seed_row(seeded_slot_cast, 60002)
_expect(
tuple(seeded_slot_a.get(key) for key in slot_descriptor_keys)
== tuple(seeded_slot_b.get(key) for key in slot_descriptor_keys),
"slot_seed should keep random character-slot descriptors stable across person-axis rerolls",
)
seeded_trace_a = seeded_slot_a.get("generation_trace") if isinstance(seeded_slot_a.get("generation_trace"), dict) else {}
seeded_trace_b = seeded_slot_b.get("generation_trace") if isinstance(seeded_slot_b.get("generation_trace"), dict) else {}
_expect(
seeded_trace_a.get("seed_axes", {}).get("person", {}).get("seed")
!= seeded_trace_b.get("seed_axes", {}).get("person", {}).get("seed"),
"slot_seed regression check should actually reroll the person axis",
)
seeded_krea_a = krea_formatter.format_krea2_prompt("", metadata_json=_json(seeded_slot_a), target="single")
seeded_krea_b = krea_formatter.format_krea2_prompt("", metadata_json=_json(seeded_slot_b), target="single")
_expect(
seeded_krea_a.get("krea_prompt") == seeded_krea_b.get("krea_prompt"),
"slot_seed should keep final Krea prompt stable across person-axis rerolls",
)
unseeded_slot_cast = slot_seed_cast(-1)
unseeded_slot_a = slot_seed_row(unseeded_slot_cast, 60001)
unseeded_slot_b = slot_seed_row(unseeded_slot_cast, 60002)
_expect(
tuple(unseeded_slot_a.get(key) for key in slot_descriptor_keys)
!= tuple(unseeded_slot_b.get(key) for key in slot_descriptor_keys),
"unseeded random character slot should still follow person-axis rerolls",
)
pose_changed = False
for reroll_seed in range(deterministic_seed + 1, deterministic_seed + 10):
pose_reroll = _prompt_row(
name="seed_config_policy_pose_reroll",
category="Hardcore sexual poses",
subcategory="Penetrative sex",
seed=deterministic_seed,
seed_config=pb.build_seed_lock_config_json(
base_seed=deterministic_seed,
reroll_axis="pose",
reroll_seed=reroll_seed,
),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
location_config=_coworking_location_config(),
)
_expect(
pose_reroll.get("cast_descriptor_text") == deterministic_a.get("cast_descriptor_text"),
"pose reroll should keep locked cast descriptors stable",
)
_expect(
pose_reroll.get("scene_text") == deterministic_a.get("scene_text"),
"pose reroll should keep locked scene stable",
)
if (
pose_reroll.get("position_key") != deterministic_a.get("position_key")
or pose_reroll.get("source_role_graph") != deterministic_a.get("source_role_graph")
or pose_reroll.get("item") != deterministic_a.get("item")
):
pose_changed = True
break
_expect(pose_changed, "pose reroll should change pose/action metadata while cast and scene stay locked")
def smoke_prompt_route_simulation_policy() -> None:
report = prompt_route_simulation.run_simulation(seed=3901, include_prompts=False)
summary = report.get("summary") or {}
quality = report.get("quality") or {}
_expect(summary.get("cases") == 14, "Prompt route simulation case count changed unexpectedly")
_expect(summary.get("coverage_checks") == 2, "Prompt route simulation lost family coverage checks")
_expect(summary.get("axis_checks") == 6, "Prompt route simulation lost axis check coverage")
_expect(summary.get("pair_seed_checks") == 7, "Prompt route simulation lost pair seed check coverage")
_expect(summary.get("issues") == 0, f"Prompt route simulation reported issues: {report.get('issues')}")
_expect(quality.get("route_cases") == 14, "Prompt route simulation quality summary lost route case count")
_expect(quality.get("route_issues") == 0, f"Prompt route simulation quality reported route issues: {quality}")
_expect(quality.get("check_issues") == 0, f"Prompt route simulation quality reported check issues: {quality}")
_expect((quality.get("targets") or {}).get("single", {}).get("cases") == 10, "Prompt route simulation quality lost single target count")
_expect((quality.get("targets") or {}).get("softcore", {}).get("cases") == 2, "Prompt route simulation quality lost softcore target count")
_expect((quality.get("targets") or {}).get("hardcore", {}).get("cases") == 2, "Prompt route simulation quality lost hardcore target count")
_expect(not quality.get("issue_buckets"), "Prompt route simulation quality should have no issue buckets on clean baseline")
_expect(not quality.get("weakest_cases"), "Prompt route simulation quality should have no weak cases on clean baseline")
cases = {case.get("name"): case for case in report.get("cases") or []}
for route_name in (
"hardcore.single.oral",
"hardcore.single.manual",
"hardcore.single.outercourse",
"hardcore.single.foreplay",
"hardcore.single.interaction",
"hardcore.single.anal",
"hardcore.single.threesome",
"hardcore.single.group",
"hardcore.single.climax",
"insta_pair.penetration.hardcore",
):
_expect(route_name in cases, f"Prompt route simulation lost route family case {route_name}")
coverage_checks = {check.get("name"): check for check in report.get("coverage_checks") or []}
action_coverage = coverage_checks.get("route_coverage.action_families") or {}
position_coverage = coverage_checks.get("route_coverage.position_families") or {}
_expect(not action_coverage.get("issues"), f"Prompt route simulation action coverage failed: {action_coverage}")
_expect(not position_coverage.get("issues"), f"Prompt route simulation position coverage failed: {position_coverage}")
expected_actions = (
set(hardcore_action_metadata.HARDCORE_ACTION_FAMILY_CHOICES)
- prompt_route_simulation.ROUTE_SIM_ACTION_FAMILY_EXCLUSIONS
)
expected_positions = (
set(hardcore_position_config.hardcore_position_family_choices())
- prompt_route_simulation.ROUTE_SIM_POSITION_FAMILY_EXCLUSIONS
)
_expect(set(action_coverage.get("observed") or []) == expected_actions, "Prompt route simulation action coverage drifted")
_expect(set(position_coverage.get("observed") or []) == expected_positions, "Prompt route simulation position coverage drifted")
pov_hard = cases.get("insta_pair.pov_outercourse.hardcore") or {}
pov_summary = pov_hard.get("summary") or {}
_expect(
pov_summary.get("position_key") == "penis_licking",
"Prompt route simulation should catch selected outercourse position as primary position_key",
)
_expect(
"penis_licking" in (pov_summary.get("position_keys") or []),
"Prompt route simulation lost selected outercourse key from position_keys",
)
axis_checks = {check.get("name"): check for check in report.get("axis_checks") or []}
for check_name in (
"seed_axis.locked_determinism",
"seed_axis.person_reroll",
"seed_axis.scene_reroll",
"seed_axis.pose_reroll",
"seed_axis.expression_reroll",
"seed_axis.composition_reroll",
):
check = axis_checks.get(check_name) or {}
_expect(check, f"Prompt route simulation lost seed-axis check {check_name}")
_expect(not check.get("issues"), f"Prompt route simulation seed-axis check reported issues: {check_name}")
_expect(axis_checks["seed_axis.locked_determinism"].get("changed") is False, "Locked determinism check should not be a reroll")
for check_name in (
"seed_axis.person_reroll",
"seed_axis.scene_reroll",
"seed_axis.pose_reroll",
"seed_axis.expression_reroll",
"seed_axis.composition_reroll",
):
_expect(axis_checks[check_name].get("changed") is True, f"{check_name} should prove its axis can reroll")
pair_seed_checks = {check.get("name"): check for check in report.get("pair_seed_checks") or []}
for check_name in (
"pair_seed.locked_determinism",
"pair_seed.person_reroll",
"pair_seed.scene_reroll",
"pair_seed.content_reroll",
"pair_seed.pose_reroll",
"pair_seed.expression_reroll",
"pair_seed.composition_reroll",
):
check = pair_seed_checks.get(check_name) or {}
_expect(check, f"Prompt route simulation lost pair seed check {check_name}")
_expect(not check.get("issues"), f"Prompt route simulation pair seed check reported issues: {check_name}")
_expect(
pair_seed_checks["pair_seed.locked_determinism"].get("changed") is False,
"Pair locked determinism check should not be a reroll",
)
for check_name in (
"pair_seed.person_reroll",
"pair_seed.scene_reroll",
"pair_seed.content_reroll",
"pair_seed.pose_reroll",
"pair_seed.expression_reroll",
"pair_seed.composition_reroll",
):
_expect(pair_seed_checks[check_name].get("changed") is True, f"{check_name} should prove its axis can reroll")
_expect(
pair_seed_checks["pair_seed.content_reroll"].get("changed") is True,
"Pair content reroll should prove soft outfit/content can reroll while hard action stays locked",
)
_expect(
pair_seed_checks["pair_seed.pose_reroll"].get("changed") is True,
"Pair pose reroll should prove hard action can reroll while soft/cast/scene axes stay locked",
)
sweep = prompt_route_simulation.run_simulation_sweep(seed=3901, count=3, seed_step=101, include_prompts=False)
sweep_summary = sweep.get("summary") or {}
sweep_quality = sweep.get("quality") or {}
_expect(sweep_summary.get("runs") == 3, "Prompt route simulation sweep lost run coverage")
_expect(sweep_summary.get("seeds") == [3901, 4002, 4103], "Prompt route simulation sweep seed sequence changed")
_expect(sweep_summary.get("cases") == 42, "Prompt route simulation sweep case count changed")
_expect(sweep_summary.get("issues") == 0, f"Prompt route simulation sweep reported issues: {sweep.get('issues')}")
_expect(sweep_quality.get("route_cases") == 42, "Prompt route simulation sweep quality lost route case count")
_expect(sweep_quality.get("route_issues") == 0, f"Prompt route simulation sweep quality reported route issues: {sweep_quality}")
_expect(sweep_quality.get("check_issues") == 0, f"Prompt route simulation sweep quality reported check issues: {sweep_quality}")
_expect((sweep_quality.get("targets") or {}).get("single", {}).get("cases") == 30, "Prompt route simulation sweep quality lost single target count")
_expect((sweep_quality.get("targets") or {}).get("softcore", {}).get("cases") == 6, "Prompt route simulation sweep quality lost softcore target count")
_expect((sweep_quality.get("targets") or {}).get("hardcore", {}).get("cases") == 6, "Prompt route simulation sweep quality lost hardcore target count")
def smoke_node_camera_registration() -> None:
required_nodes = [
"SxCPCameraControl",
"SxCPCameraOrbitControl",
"SxCPQwenCameraTranslator",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
camera_control = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCameraControl"]
camera_inputs = camera_control.INPUT_TYPES().get("required") or {}
_expect("camera_mode" in camera_inputs, "Camera Control lost camera_mode input")
_expect("tooltip" in camera_inputs["camera_mode"][1], "Camera Control tooltip injection missing")
camera_config = camera_control().build(
"handheld_selfie",
"three_quarter_body",
"high_angle",
"smartphone_wide",
"arm_length",
"vertical_story",
"phone_visible",
"locked",
"compact",
)[0]
parsed_camera = json.loads(camera_config)
_expect(parsed_camera.get("camera_mode") == "handheld_selfie", "Camera Control lost camera_mode")
_expect(parsed_camera.get("phone_visibility") == "phone_visible", "Camera Control lost phone visibility")
orbit_config, orbit_prompt, orbit_info = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCameraOrbitControl"]().build(
True,
"standard",
45,
0,
5.5,
"from_zoom",
"auto",
"auto",
"auto",
"auto",
"soft_hint",
"compact",
True,
)
parsed_orbit = json.loads(orbit_config)
_expect(parsed_orbit.get("camera_source") == "orbit", "Orbit camera lost source metadata")
_expect("front-right quarter view" in orbit_prompt, "Orbit camera prompt lost direction")
_expect(json.loads(orbit_info).get("orbit_azimuth") == 45, "Orbit info JSON lost azimuth")
qwen_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPQwenCameraTranslator"]
qwen_inputs = qwen_node.INPUT_TYPES()
_expect("camera_info" in (qwen_inputs.get("optional") or {}), "Qwen translator lost camera_info optional input")
qwen_config, qwen_prompt, qwen_info = qwen_node().build(
"<sks> front-right quarter view eye-level shot medium shot",
True,
"standard",
"auto",
"auto",
"auto",
"auto",
"soft_hint",
"compact",
False,
True,
)
parsed_qwen = json.loads(qwen_config)
_expect(parsed_qwen.get("camera_source") == "qwen_multiangle_prompt", "Qwen translator lost source metadata")
_expect(parsed_qwen.get("phone_visibility") == "auto", "Qwen translator should suppress phone visibility by default")
_expect("front-right quarter view" in qwen_prompt, "Qwen camera prompt lost direction")
_expect(json.loads(qwen_info).get("qwen_prompt", "").startswith("<sks>"), "Qwen info JSON lost original prompt")
def smoke_node_route_config_registration() -> None:
required_nodes = [
"SxCPCategoryPreset",
"SxCPLocationPool",
"SxCPCompositionPool",
"SxCPLocationTheme",
"SxCPStylePool",
"SxCPCastControl",
"SxCPCastBias",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
category_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCategoryPreset"]
category_inputs = category_node.INPUT_TYPES().get("required") or {}
_expect("preset" in category_inputs, "Category Preset lost preset input")
_expect("tooltip" in category_inputs["preset"][1], "Category Preset tooltip injection missing")
category_config, category, subcategory = category_node().build("auto_weighted", "random")
parsed_category = json.loads(category_config)
_expect(category == parsed_category.get("category") == "auto_weighted", "Category Preset output category mismatch")
_expect(subcategory == "random", "Category Preset output subcategory mismatch")
location_config, location_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPLocationPool"]().build(
True,
"replace",
"custom_only",
"classical library stacks with brass lamps",
)
parsed_location = json.loads(location_config)
_expect(parsed_location.get("scene_entries"), "Location Pool did not keep custom location")
_expect("locations=1" in location_summary, "Location Pool summary lost custom count")
composition_config, composition_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCompositionPool"]().build(
True,
"replace",
"no_outfit_check",
"long aisle composition with shelves repeating behind the subject",
)
parsed_composition = json.loads(composition_config)
_expect(parsed_composition.get("composition_entries"), "Composition Pool did not keep composition entries")
_expect("compositions=" in composition_summary, "Composition Pool summary lost composition count")
theme_location, theme_composition, theme_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPLocationTheme"]().build(
True,
"replace",
"semi_public_affair",
"",
"",
)
_expect(json.loads(theme_location).get("scene_entries"), "Location Theme did not output locations")
_expect(json.loads(theme_composition).get("composition_entries"), "Location Theme did not output compositions")
_expect("semi_public_affair" in theme_summary, "Location Theme summary lost theme name")
style_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPStylePool"]
style_inputs = style_node.INPUT_TYPES().get("required") or {}
_expect("preset" in style_inputs, "Style Pool lost preset input")
_expect("tooltip" in style_inputs["preset"][1], "Style Pool tooltip injection missing")
style_config, style_summary = style_node().build(
True,
"replace",
"comic_pinup_colored_pencil",
"",
"",
"",
)
parsed_style = json.loads(style_config)
_expect(parsed_style.get("schema") == "sxcp_style_config_v1", "Style Pool emitted wrong schema")
_expect("comic pin-up" in parsed_style.get("style", ""), "Style Pool lost comic preset style")
_expect("comic pin-up" in style_summary, "Style Pool summary lost preset label")
cast_config, women_count, men_count, cast_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCastControl"]().build(
"mixed_couple",
1,
1,
)
parsed_cast = json.loads(cast_config)
_expect((women_count, men_count) == (parsed_cast.get("women_count"), parsed_cast.get("men_count")), "Cast Control count outputs mismatch")
_expect("1 women, 1 men" in cast_summary, "Cast Control summary changed unexpectedly")
cast_bias = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCastBias"]()
bias_a = cast_bias.build(123, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman")
bias_b = cast_bias.build(123, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman")
_expect(bias_a == bias_b, "Cast Bias should be deterministic for fixed seed and row")
_expect(bias_a[1] + bias_a[2] >= 1, "Cast Bias empty behavior allowed empty cast")
_expect("weighted cast:" in bias_a[3], "Cast Bias summary lost weighted cast label")
bias_seed_config = json.dumps({"category": 702})
seeded_bias_a = cast_bias.build(-1, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman", bias_seed_config)
seeded_bias_b = cast_bias.build(-1, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman", bias_seed_config)
_expect(seeded_bias_a == seeded_bias_b, "Cast Bias should honor seed_config aliases deterministically")
_expect(cast_bias._configured_cast_seed({"category": "702"}) == 702, "Cast Bias seed should delegate through category aliases")
def smoke_node_character_registration() -> None:
required_nodes = [
"SxCPHairLength",
"SxCPHairColor",
"SxCPHairStyle",
"SxCPCharacterAgeRange",
"SxCPCharacterBodyPool",
"SxCPWomanBodyPool",
"SxCPManBodyPool",
"SxCPEyeColorPool",
"SxCPCharacterClothing",
"SxCPCharacterManualDetails",
"SxCPWomanSlot",
"SxCPManSlot",
"SxCPCharacterSlot",
"SxCPCharacterProfileSave",
"SxCPCharacterProfileLoad",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
woman_slot_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPWomanSlot"]
woman_slot_input_types = woman_slot_node.INPUT_TYPES()
woman_slot_inputs = woman_slot_input_types.get("required") or {}
_expect("slot_seed" in woman_slot_inputs, "Woman Slot lost slot_seed input")
_expect("tooltip" in woman_slot_inputs["slot_seed"][1], "Woman Slot tooltip injection missing")
woman_slot_optional = woman_slot_input_types.get("optional") or {}
cast_tooltip = woman_slot_optional.get("character_cast", (None, {}))[1].get("tooltip", "")
_expect("incoming cast from the previous slot" in cast_tooltip, "Woman Slot character_cast tooltip should explain chain order")
man_slot_required = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPManSlot"].INPUT_TYPES().get("required") or {}
presence_tooltip = man_slot_required.get("presence_mode", (None, {}))[1].get("tooltip", "")
_expect("first-person viewer" in presence_tooltip, "Man Slot presence tooltip should explain POV behavior")
hair_config, hair_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPHairColor"]().build(
"replace_axis",
"",
include_blonde=True,
)
parsed_hair = json.loads(hair_config)
_expect(parsed_hair.get("colors") == ["blonde"], "Hair Color did not output selected blonde pool")
_expect("colors=blonde" in hair_summary, "Hair Color summary changed unexpectedly")
age_config, age_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterAgeRange"]().build(
"replace_axis",
25,
27,
)
parsed_age = json.loads(age_config)
_expect(parsed_age.get("ages") == ["25-year-old adult", "26-year-old adult", "27-year-old adult"], "Age Range output changed")
_expect("25-year-old adult" in age_summary, "Age Range summary lost selected ages")
body_config, _body_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPWomanBodyPool"]().build(
"replace_axis",
"",
include_curvy=True,
)
_expect(json.loads(body_config).get("bodies") == ["curvy"], "Woman Body Pool did not output selected body")
eye_config, _eye_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPEyeColorPool"]().build(
"replace_axis",
"",
include_blue=True,
)
_expect(json.loads(eye_config).get("eyes") == ["blue"], "Eye Color Pool did not output selected eye color")
clothing_config, clothing_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterClothing"]().build(
"replace_axis",
"lingerie_tease",
"fully_nude",
"",
"",
)
parsed_clothing = json.loads(clothing_config)
_expect(parsed_clothing.get("softcore_outfits"), "Character Clothing lost softcore outfit pool")
_expect(parsed_clothing.get("hardcore_clothing") == ["fully nude"], "Character Clothing lost hardcore clothing state")
_expect("soft_outfits=" in clothing_summary, "Character Clothing summary lost outfit count")
manual_config, manual_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterManualDetails"]().build(
"merge_nonempty",
"31-year-old adult",
"curvy",
"custom body phrase",
"warm skin",
"short blonde hair",
"blue eyes",
"red dress",
"fully nude",
)
parsed_manual = json.loads(manual_config)
_expect(parsed_manual.get("manual_age") == "31-year-old adult", "Manual Details lost manual_age")
_expect(parsed_manual.get("softcore_outfit") == "red dress", "Manual Details lost softcore outfit")
_expect("manual_age=31-year-old adult" in manual_summary, "Manual Details summary changed unexpectedly")
cast, slot, slot_summary, slot_status = woman_slot_node().build(
True,
"A",
123,
"25-year-old adult",
"western_european",
"balanced",
"curvy",
"full",
True,
0.5,
0.4,
0.8,
"",
"",
"",
hair_config,
"",
)
parsed_slot = json.loads(slot)
_expect(parsed_slot.get("subject_type") == "woman", "Woman Slot output lost subject type")
_expect(parsed_slot.get("slot_seed") == 123, "Woman Slot output lost slot seed")
_expect("Woman A" in slot_summary, "Woman Slot summary lost label")
_expect("1 slot(s)" in slot_status, "Woman Slot status lost cast count")
_expect(json.loads(cast).get("slots"), "Woman Slot did not output chained cast JSON")
man_cast, man_slot, _man_summary, _man_status = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPManSlot"]().build(
True,
"A",
124,
"40-year-old adult",
"western_european",
"average",
"compact",
True,
0.3,
"pov",
0.2,
0.7,
cast,
"",
"",
"",
"",
)
_expect(json.loads(man_slot).get("presence_mode") == "pov", "Man Slot output lost POV presence mode")
_expect(len(json.loads(man_cast).get("slots") or []) == 2, "Man Slot did not append to incoming cast")
save_result = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterProfileSave"]().build(
"smoke_profile",
"character_slot",
"woman",
"",
"",
"",
"",
"",
"",
"",
False,
character_slot=slot,
)
saved_profile = save_result["result"][0]
_expect(save_result["result"][2] == "smoke_profile", "Profile Save lost profile name")
_expect(save_result["result"][4] == "not_saved", "Profile Save should not write when save_now is false")
loaded_profile = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterProfileLoad"]().build(
True,
"manual",
"",
False,
False,
fallback_profile_json=saved_profile,
)
_expect(loaded_profile[4] == "fallback", "Profile Load should consume fallback profile JSON")
_expect(json.loads(loaded_profile[0]).get("profile_type") == "character", "Profile Load returned wrong profile type")
def smoke_node_hardcore_position_registration() -> None:
required_nodes = [
"SxCPHardcorePositionPool",
"SxCPHardcoreActionFilter",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
position_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPHardcorePositionPool"]
position_inputs = position_node.INPUT_TYPES().get("required") or {}
_expect("family" in position_inputs, "Hardcore Position Pool lost family input")
_expect("tooltip" in position_inputs["family"][1], "Hardcore Position Pool tooltip injection missing")
pool_config, pool_summary = position_node().build(
"replace",
"oral",
"",
include_boobjob=True,
include_handjob=True,
)
parsed_pool = json.loads(pool_config)
_expect(parsed_pool.get("family") == "oral", "Hardcore Position Pool lost selected family")
_expect(parsed_pool.get("positions") == ["boobjob", "handjob"], "Hardcore Position Pool lost selected positions")
_expect("positions=boobjob,handjob" in pool_summary, "Hardcore Position Pool summary changed unexpectedly")
filter_config, filter_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPHardcoreActionFilter"]().build(
"outercourse_only",
False,
False,
False,
True,
True,
True,
False,
True,
False,
False,
pool_config,
)
parsed_filter = json.loads(filter_config)
_expect(parsed_filter.get("family") == "outercourse", "Hardcore Action Filter did not apply focus family")
_expect(parsed_filter.get("positions") == ["boobjob", "handjob"], "Hardcore Action Filter lost incoming positions")
_expect(parsed_filter.get("allow_penetration") is False, "Hardcore Action Filter did not block penetration")
_expect(parsed_filter.get("allow_outercourse") is True, "Hardcore Action Filter should allow outercourse")
_expect("blocked=" in filter_summary, "Hardcore Action Filter summary lost blocked-gate details")
def smoke_node_formatter_registration() -> None:
required_nodes = [
"SxCPCaptionNaturalizer",
"SxCPKrea2Formatter",
"SxCPSDXLFormatter",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
krea_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2Formatter"]
krea_inputs = krea_node.INPUT_TYPES().get("required") or {}
_expect("source_text" in krea_inputs, "Krea2 Formatter lost source_text input")
_expect("tooltip" in krea_inputs["source_text"][1], "Krea2 Formatter tooltip injection missing")
krea_optional = krea_node.INPUT_TYPES().get("optional") or {}
_expect("source_text_input" in krea_optional, "Krea2 Formatter lost connectable source_text_input")
_expect("metadata_json" in krea_optional, "Krea2 Formatter lost metadata_json input")
_expect(krea_optional["metadata_json"][1].get("forceInput") is True, "Krea2 Formatter metadata_json should be connectable")
_expect(krea_optional["negative_prompt"][1].get("forceInput") is True, "Krea2 Formatter negative_prompt should be connectable")
caption, caption_method, caption_trace_json = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"]().build(
"A woman standing by a window, best quality",
"caption_or_prompt",
"manual_controls",
"concise",
"drop_style_tail",
"sxcppnl7",
True,
)
_expect_text("node_formatter.caption", caption, 20)
_expect(caption.startswith("sxcppnl7"), "Caption Naturalizer did not prepend trigger")
_expect("text(" in caption_method, "Caption Naturalizer method changed unexpectedly")
caption_trace = json.loads(_expect_text("node_formatter.caption_trace", caption_trace_json, 20))
_expect(caption_trace.get("formatter") == "caption", "Caption Naturalizer trace lost formatter")
_expect(caption_trace.get("branch") == "text", "Caption Naturalizer trace lost text branch")
caption_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"].INPUT_TYPES().get("required") or {}
_expect("caption_profile" in caption_inputs, "Caption Naturalizer lost caption_profile input")
_expect("target" in caption_inputs, "Caption Naturalizer lost target input")
_expect(caption_inputs["target"][0] == formatter_target.target_choices(), "Caption Naturalizer target choices drifted")
_expect(caption_inputs["detail_level"][0] == formatter_detail.detail_level_choices(), "Caption Naturalizer detail choices drifted")
_expect(caption_inputs["style_policy"][0] == caption_policy.style_policy_choices(), "Caption Naturalizer style choices drifted")
_expect("tooltip" in caption_inputs["caption_profile"][1], "Caption profile tooltip injection missing")
_expect(krea_inputs["target"][0] == formatter_target.target_choices(), "Krea2 Formatter target choices drifted")
_expect(krea_inputs["detail_level"][0] == formatter_detail.detail_level_choices(), "Krea2 Formatter detail choices drifted")
_expect(krea_inputs["style_mode"][0] == krea_format_route.style_mode_choices(), "Krea2 Formatter style choices drifted")
krea_output = krea_node().build(
"sxcppnl7 A woman standing by a window",
"prompt",
"single",
"concise",
"preserve",
True,
)
_expect_text("node_formatter.krea_prompt", krea_output[0], 20)
_expect("sxcppnl7" in krea_output[0], "Krea2 Formatter did not preserve trigger when enabled")
_expect(krea_output[6].startswith("text("), "Krea2 Formatter method changed unexpectedly")
krea_trace = json.loads(_expect_text("node_formatter.krea_trace", krea_output[7], 20))
_expect(krea_trace.get("formatter") == "krea2", "Krea2 Formatter trace lost formatter")
_expect(krea_trace.get("branch") == "fallback", "Krea2 Formatter trace lost fallback branch")
sdxl_output = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"]().build(
"A woman standing by a window",
"prompt",
"single",
"manual_controls",
"flat_vector_pony",
"pony_high",
"mythp0rt",
True,
False,
1.29,
)
_expect_text("node_formatter.sdxl_prompt", sdxl_output[0], 40)
_expect_trigger_once("node_formatter.sdxl_prompt", sdxl_output[0], "mythp0rt")
_expect_text("node_formatter.sdxl_negative", sdxl_output[1], 20)
_expect(sdxl_output[6].startswith("text("), "SDXL Formatter method changed unexpectedly")
sdxl_trace = json.loads(_expect_text("node_formatter.sdxl_trace", sdxl_output[7], 20))
_expect(sdxl_trace.get("formatter") == "sdxl", "SDXL Formatter trace lost formatter")
_expect(sdxl_trace.get("branch") == "fallback", "SDXL Formatter trace lost fallback branch")
sdxl_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"].INPUT_TYPES().get("required") or {}
_expect("formatter_profile" in sdxl_inputs, "SDXL Formatter lost formatter_profile input")
_expect(sdxl_inputs["target"][0] == formatter_target.target_choices(), "SDXL Formatter target choices drifted")
_expect("tooltip" in sdxl_inputs["formatter_profile"][1], "SDXL formatter_profile tooltip injection missing")
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=4311,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
)
pair_metadata = _json(pair)
expected_krea_pair = krea_formatter.format_krea2_prompt(
"",
metadata_json=pair_metadata,
input_hint="metadata_json",
target="hardcore",
detail_level="balanced",
style_mode="preserve",
extra_positive="node krea pair marker",
extra_negative="node krea pair negative",
)
node_krea_pair = krea_node().build(
"",
"metadata_json",
"hardcore",
"balanced",
"preserve",
False,
metadata_json=pair_metadata,
extra_positive="node krea pair marker",
extra_negative="node krea pair negative",
)
_expect(
node_krea_pair
== (
expected_krea_pair["krea_prompt"],
expected_krea_pair["negative_prompt"],
expected_krea_pair["krea_softcore_prompt"],
expected_krea_pair["krea_hardcore_prompt"],
expected_krea_pair["softcore_negative_prompt"],
expected_krea_pair["hardcore_negative_prompt"],
expected_krea_pair["method"],
expected_krea_pair["route_trace_json"],
),
"Krea2 Formatter node output drifted from public metadata formatter",
)
expected_sdxl_pair = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=pair_metadata,
input_hint="metadata_json",
target="hardcore",
formatter_profile="manual_controls",
style_preset="flat_vector_pony",
quality_preset="pony_high",
trigger=SdxlTrigger,
prepend_trigger=True,
preserve_trigger=False,
nude_weight=1.29,
extra_positive="node sdxl pair marker",
extra_negative="node sdxl pair negative",
)
node_sdxl_pair = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"]().build(
"",
"metadata_json",
"hardcore",
"manual_controls",
"flat_vector_pony",
"pony_high",
SdxlTrigger,
True,
False,
1.29,
metadata_json=pair_metadata,
extra_positive="node sdxl pair marker",
extra_negative="node sdxl pair negative",
)
_expect(
node_sdxl_pair
== (
expected_sdxl_pair["sdxl_prompt"],
expected_sdxl_pair["negative_prompt"],
expected_sdxl_pair["sdxl_softcore_prompt"],
expected_sdxl_pair["sdxl_hardcore_prompt"],
expected_sdxl_pair["softcore_negative_prompt"],
expected_sdxl_pair["hardcore_negative_prompt"],
expected_sdxl_pair["method"],
expected_sdxl_pair["route_trace_json"],
),
"SDXL Formatter node output drifted from public metadata formatter",
)
expected_caption_pair = caption_naturalizer.naturalize_caption_with_trace(
"",
metadata_json=pair_metadata,
input_hint="metadata_json",
target="hardcore",
trigger=Trigger,
include_trigger=True,
detail_level="balanced",
style_policy="drop_style_tail",
caption_profile="training_dense",
)
node_caption_pair = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"]().build(
"",
"metadata_json",
"training_dense",
"balanced",
"drop_style_tail",
Trigger,
True,
"hardcore",
metadata_json=pair_metadata,
)
_expect(
node_caption_pair == expected_caption_pair,
"Caption Naturalizer node output drifted from public metadata formatter",
)
def smoke_node_insta_registration() -> None:
required_nodes = [
"SxCPInstaOFOptions",
"SxCPInstaOFPromptPair",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
options_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPInstaOFOptions"]
options_inputs = options_node.INPUT_TYPES().get("required") or {}
_expect("hardcore_detail_density" in options_inputs, "Insta/OF Options lost hardcore_detail_density input")
_expect("tooltip" in options_inputs["hardcore_detail_density"][1], "Insta/OF Options tooltip injection missing")
options_json = options_node().build(
"same_as_hardcore",
"couple",
1,
1,
"lingerie_tease",
"hardcore",
True,
True,
0.45,
0.85,
"hybrid",
"same_creator_same_room",
"explicit_nude",
"standard",
"standard",
"compact",
"balanced",
)[0]
parsed_options = json.loads(options_json)
_expect(parsed_options.get("softcore_cast") == "same_as_hardcore", "Insta/OF Options lost softcore cast")
_expect(parsed_options.get("hardcore_cast") == "couple", "Insta/OF Options lost hardcore cast")
_expect(parsed_options.get("hardcore_clothing_continuity") == "explicit_nude", "Insta/OF Options lost clothing continuity")
pair_output = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPInstaOFPromptPair"]().build(
1,
41,
123,
"any",
"random",
Trigger,
True,
options_json=options_json,
)
_expect_text("node_insta.softcore_prompt", pair_output[0], 20)
_expect_text("node_insta.hardcore_prompt", pair_output[1], 20)
pair = json.loads(pair_output[7])
_expect_pair(pair, "node_insta_pair")
_expect(pair.get("options", {}).get("hardcore_cast") == "couple", "Insta/OF Prompt Pair lost options metadata")
def smoke_node_scene_chain_registration() -> None:
required_nodes = [
"SxCPSceneLayerSeedOptions",
"SxCPSceneCastOptions",
"SxCPSceneCharacterOptions",
"SxCPSceneWardrobeOptions",
"SxCPSceneLocationLayoutOptions",
"SxCPSceneSetDressingOptions",
"SxCPSceneBlockingOptions",
"SxCPSceneActionOptions",
"SxCPScenePerformanceOptions",
"SxCPSceneCameraOptions",
"SxCPSceneCompositionOptions",
"SxCPSceneLightingOptions",
"SxCPSceneBranchOptions",
"SxCPSceneStart",
"SxCPSceneCast",
"SxCPSceneCharacter",
"SxCPSceneWardrobe",
"SxCPSceneLocation",
"SxCPSceneSetDressing",
"SxCPSceneBlocking",
"SxCPSceneAction",
"SxCPScenePerformance",
"SxCPSceneCamera",
"SxCPSceneComposition",
"SxCPSceneLighting",
"SxCPSceneBranchPair",
"SxCPSoftcoreBranchOptions",
"SxCPHardcoreBranchOptions",
"SxCPSceneOutput",
"SxCPScenePairOutput",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
nodes = sxcp_nodes.NODE_CLASS_MAPPINGS
scene, start_summary, _start_metadata = nodes["SxCPSceneStart"]().build(
1,
41,
777,
"raw",
"woman",
"random",
"balanced",
Trigger,
True,
)
_expect("scene v" in start_summary, "Scene Start summary changed unexpectedly")
parsed_scene = json.loads(scene)
_expect(parsed_scene.get("schema") == "sxcp_scene_v2", "Scene Start did not emit v2 schema")
cast_options = nodes["SxCPSceneCastOptions"]().build("replace", "mixed_couple", 1, 1, "woman_a", "none")[0]
scene, _cast_config, _cast_summary, _cast_metadata = nodes["SxCPSceneCast"]().build(
scene,
"mixed_couple",
1,
1,
"woman_a",
"none",
cast_options=cast_options,
)
character_options = nodes["SxCPSceneCharacterOptions"]().build(
"replace",
"medium",
"visible",
"enabled",
0.5,
-1,
-1,
"controlled slot performance note",
)[0]
scene, character_cast, _slot, _summary, _metadata = nodes["SxCPSceneCharacter"]().build(
scene,
True,
"woman",
"A",
-1,
"25-year-old adult",
"random",
"random",
"random",
"medium",
True,
0.5,
"visible",
-1,
-1,
character_options=character_options,
)
scene, character_cast, _slot, _summary, _metadata = nodes["SxCPSceneCharacter"]().build(
scene,
True,
"man",
"A",
-1,
"40-year-old adult",
"random",
"random",
"average",
"compact",
True,
0.5,
"visible",
-1,
-1,
)
wardrobe_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build("wardrobe", "fixed", 9981, "content", "same_for_all_rows", "replace_layer")[0]
wardrobe_options = nodes["SxCPSceneWardrobeOptions"]().build(
"replace",
"woman",
"A",
"full",
"explicit_nude",
True,
"simple black dress",
"",
"thin necklace",
"",
)[0]
scene, character_cast, _wardrobe_summary, _wardrobe_metadata = nodes["SxCPSceneWardrobe"]().build(
scene,
True,
"woman",
"A",
"full",
"simple black dress",
"fully nude",
"",
wardrobe_options=wardrobe_options,
seed_options=wardrobe_seed_options,
)
slots = json.loads(character_cast).get("slots") or []
woman_slot = next(slot for slot in slots if slot.get("subject_type") == "woman")
_expect(woman_slot.get("softcore_outfit") == "simple black dress", "Scene Wardrobe did not update softcore outfit")
_expect(woman_slot.get("hardcore_clothing") == "fully nude", "Scene Wardrobe did not update hardcore clothing")
_expect(json.loads(scene).get("seed_trace", {}).get("wardrobe", {}).get("seed") == 9981, "Scene Wardrobe seed options did not write seed trace")
location_options = nodes["SxCPSceneLocationLayoutOptions"]().build(
"replace",
"mirror edge",
"soft curtain layer",
"repeating lamp reflections",
"private",
"private",
"warm mirror-room geometry",
)[0]
scene = nodes["SxCPSceneLocation"]().build(
scene,
True,
"replace",
"custom_only",
"quiet studio room with a large mirror",
"",
location_options=location_options,
)[0]
set_options = nodes["SxCPSceneSetDressingOptions"]().build("replace", "mirror edge", "soft curtains", "small lamp", "warm fabric texture", "")[0]
scene = nodes["SxCPSceneSetDressing"]().build(scene, True, "mirror edge", "soft curtains", "small lamp", "", set_options=set_options)[0]
blocking_options = nodes["SxCPSceneBlockingOptions"]().build(
"replace",
"standing",
"woman near mirror",
"man behind her",
"three_quarter",
"foreground",
"standing close",
"",
)[0]
scene = nodes["SxCPSceneBlocking"]().build(scene, True, "standing", "woman near mirror", "man behind her", "", blocking_options=blocking_options)[0]
action_options = nodes["SxCPSceneActionOptions"]().build("replace", "softcore", "softcore_tease", "no_change", "quiet pose transition")[0]
scene = nodes["SxCPSceneAction"]().build(scene, True, "regular", "no_change", "", action_options=action_options)[0]
performance_options = nodes["SxCPScenePerformanceOptions"]().build("replace", "enabled", "fixed", 0.4, "camera", "on_body", "posed", "controlled eye contact")[0]
scene = nodes["SxCPScenePerformance"]().build(scene, True, "fixed", 0.4, "controlled eye contact", performance_options=performance_options)[0]
camera_options = nodes["SxCPSceneCameraOptions"]().build("replace", "from_camera_config", True, "mirror-aware camera note")[0]
scene = nodes["SxCPSceneCamera"]().build(
scene,
True,
"standard",
"three_quarter",
"eye_level",
"auto",
"auto",
"auto",
"auto",
"strong",
"compact",
"",
camera_options=camera_options,
)[0]
composition_options = nodes["SxCPSceneCompositionOptions"]().build("replace", "body", "three_quarter", "clear", "mirror-aware three-quarter frame")[0]
scene = nodes["SxCPSceneComposition"]().build(scene, True, "replace", "no_outfit_check", "", "", composition_options=composition_options)[0]
lighting_options = nodes["SxCPSceneLightingOptions"]().build("replace", "practical_lamps", "soft", "medium", "warm", "evening", "")[0]
scene = nodes["SxCPSceneLighting"]().build(scene, True, "practical_lamps", "soft", "medium", "warm", "", lighting_options=lighting_options)[0]
output = nodes["SxCPSceneOutput"]().build(scene)
_expect_text("node_scene_chain.prompt", output[0], 40)
_expect_trigger_once("node_scene_chain.prompt", output[0], Trigger)
row = json.loads(output[3])
_expect(row.get("scene_chain", {}).get("schema") == "sxcp_scene_v2", "Scene Output lost scene_chain metadata")
branch_options = nodes["SxCPSceneBranchOptions"]().build("replace", "both", "same_creator_same_room", "hybrid", "shared branch note")[0]
branch_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build("hardcore_branch", "fixed", 7799, "pose", "same_for_all_rows", "replace_layer")[0]
soft_scene, hard_scene, _branch_summary, _branch_metadata = nodes["SxCPSceneBranchPair"]().build(
scene,
"same_creator_same_room",
"hybrid",
branch_options=branch_options,
seed_options=branch_seed_options,
)
soft_scene = nodes["SxCPSoftcoreBranchOptions"]().build(
soft_scene,
"same_as_hardcore",
"lingerie_tease",
True,
0.45,
"from_camera_config",
"compact",
"",
branch_options=branch_options,
)[0]
hard_scene = nodes["SxCPHardcoreBranchOptions"]().build(
hard_scene,
"couple",
1,
1,
"hardcore",
True,
0.85,
"explicit_nude",
"from_camera_config",
"compact",
"balanced",
"",
branch_options=branch_options,
seed_options=branch_seed_options,
)[0]
pair_output = nodes["SxCPScenePairOutput"]().build(soft_scene, hard_scene)
_expect_text("node_scene_chain.softcore_prompt", pair_output[0], 40)
_expect_text("node_scene_chain.hardcore_prompt", pair_output[1], 40)
pair = json.loads(pair_output[7])
_expect_pair(pair, "node_scene_chain_pair")
_expect(pair.get("options", {}).get("hardcore_cast") == "couple", "Scene Pair Output lost hardcore branch options")
_expect("scene_chain" in pair, "Scene Pair Output lost scene_chain metadata")
_expect(
pair.get("scene_chain", {}).get("hardcore", {}).get("seed_trace", {}).get("hardcore.hardcore_branch", {}).get("seed") == 7799,
"Scene branch seed options did not write hardcore branch seed trace",
)
hard_seed_config = pair.get("hardcore_row", {}).get("seed_config") if isinstance(pair.get("hardcore_row"), dict) else {}
_expect(hard_seed_config.get("pose_seed") == 7799, "Scene Pair Output did not pass hardcore branch pose seed to generator")
_expect(hard_seed_config.get("role_seed") == 7799, "Scene Pair Output did not pass hardcore branch role seed to generator")
hard_trace_axes = pair.get("hardcore_row", {}).get("generation_trace", {}).get("seed_axes", {}) if isinstance(pair.get("hardcore_row"), dict) else {}
_expect(
hard_trace_axes.get("pose", {}).get("seed") == 7799,
"Scene Pair Output generation trace did not use hardcore branch pose seed",
)
def smoke_node_builder_registration() -> None:
required_nodes = [
"SxCPPromptBuilder",
"SxCPPromptBuilderFromConfigs",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
builder_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPPromptBuilder"]
builder_inputs = builder_node.INPUT_TYPES().get("required") or {}
_expect("category" in builder_inputs, "Prompt Builder lost category input")
_expect("tooltip" in builder_inputs["category"][1], "Prompt Builder tooltip injection missing")
direct_output = builder_node().build(
"woman",
"random",
1,
41,
123,
"full",
"any",
"standard",
True,
0.5,
0.0,
"random",
1,
0,
-1,
-1,
Trigger,
True,
)
direct_row = json.loads(direct_output[3])
_expect_row_base(direct_row, "node_builder.direct_row")
_expect(direct_output[0] == direct_row.get("prompt"), "Prompt Builder prompt output drifted from metadata")
_expect(direct_output[4] == direct_row.get("main_category"), "Prompt Builder category output drifted from metadata")
direct_trace = direct_row.get("generation_trace")
_expect(isinstance(direct_trace, dict), "Prompt Builder metadata lost generation_trace")
_expect(direct_trace.get("branch") == "built_in", "Prompt Builder metadata generation_trace lost branch")
_expect(direct_trace.get("seed_axes", {}).get("content", {}).get("source") == "main", "Prompt Builder metadata trace lost content seed source")
_expect_trigger_once("node_builder.direct_prompt", direct_output[0], Trigger)
config_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPPromptBuilderFromConfigs"]
config_inputs = config_node.INPUT_TYPES()
_expect("category_config" in (config_inputs.get("optional") or {}), "Prompt Builder From Configs lost category_config input")
config_output = config_node().build(
1,
41,
123,
category_config=pb.build_category_config_json("woman", "random"),
cast_config=pb.build_cast_config_json("solo_woman", 1, 0),
generation_profile=pb.build_generation_profile_json(profile="balanced"),
)
config_row = json.loads(config_output[3])
_expect_row_base(config_row, "node_builder.config_row")
_expect(config_output[0] == config_row.get("prompt"), "Prompt Builder From Configs prompt output drifted from metadata")
_expect(config_output[4] == config_row.get("main_category"), "Prompt Builder From Configs category output drifted from metadata")
config_trace = config_row.get("generation_trace")
_expect(isinstance(config_trace, dict), "Prompt Builder From Configs metadata lost generation_trace")
_expect(config_trace.get("builder") == "prompt_builder", "Prompt Builder From Configs trace lost builder label")
_expect_text("node_builder.config_caption", config_output[2], 20)
def smoke_node_profile_filter_registration() -> None:
required_nodes = [
"SxCPGenerationProfile",
"SxCPAdvancedFilters",
"SxCPEthnicityList",
]
for node_name in required_nodes:
_expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
_expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
profile_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPGenerationProfile"]
profile_inputs = profile_node.INPUT_TYPES().get("required") or {}
_expect("profile" in profile_inputs, "Generation Profile lost profile input")
_expect("tooltip" in profile_inputs["profile"][1], "Generation Profile tooltip injection missing")
profile_config, profile_summary = profile_node().build(
profile="balanced",
clothing_override="profile_default",
poses_override="profile_default",
expression_enabled=True,
expression_intensity_mode="fixed",
expression_intensity=0.5,
backside_bias=-1,
minimal_clothing_ratio=-1,
standard_pose_ratio=-1,
trigger_policy="profile_default",
)
parsed_profile = json.loads(profile_config)
_expect(parsed_profile.get("profile") == "balanced", "Generation Profile output lost profile")
_expect(parsed_profile.get("expression_intensity") == 0.5, "Generation Profile output lost fixed expression intensity")
_expect("balanced:" in profile_summary, "Generation Profile summary changed unexpectedly")
filter_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPAdvancedFilters"]().build(
include_european=True,
include_mediterranean_mena=False,
include_latina=False,
include_east_asian=False,
include_southeast_asian=False,
include_south_asian=False,
include_black_african=True,
include_indigenous=False,
include_mixed=False,
include_plus_size=False,
figure="curvy",
)[0]
parsed_filter = json.loads(filter_config)
_expect(parsed_filter.get("figure") == "curvy", "Advanced Filters lost figure")
_expect(parsed_filter.get("ethnicity_includes") == ["european", "black_african"], "Advanced Filters ethnicity includes changed")
_expect(parsed_filter.get("no_plus_women") is True, "Advanced Filters should set no_plus_women when plus size is excluded")
ethnicity, ethnicity_filter_config, ethnicity_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPEthnicityList"]().build(
include_european=False,
include_mediterranean_mena=False,
include_latina=False,
include_east_asian=False,
include_southeast_asian=False,
include_south_asian=False,
include_black_african=False,
include_indigenous=False,
include_mixed=False,
include_asian=False,
include_white_asian=False,
include_western_european=False,
include_french_european=True,
include_germanic_european=False,
include_nordic_european=False,
include_celtic_european=False,
include_slavic_european=False,
include_baltic_european=False,
include_alpine_european=False,
include_balkan_european=False,
include_greek_mediterranean=False,
include_italian_mediterranean=False,
include_iberian_mediterranean=False,
strict_excludes=True,
)
parsed_ethnicity_filter = json.loads(ethnicity_filter_config)
_expect("french_european" in ethnicity, "Ethnicity List output lost selected regional ethnicity")
_expect(parsed_ethnicity_filter.get("ethnicity_includes") == ["french_european"], "Ethnicity List filter output changed")
_expect("ethnicity list:" in ethnicity_summary, "Ethnicity List summary changed unexpectedly")
SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
("builtin_single_woman", smoke_builtin_single),
("builtin_couple_metadata", smoke_builtin_couple_metadata),
("camera_scene_single", smoke_camera_scene_single),
("scene_camera_adapter_pov_profile_policy", smoke_scene_camera_adapter_pov_profile_policy),
("row_camera_policy", smoke_row_camera_policy),
("config_route_location_theme", smoke_config_route_location_theme),
("builder_prompt_route_policy", smoke_builder_prompt_route_policy),
("builder_config_route_policy", smoke_builder_config_route_policy),
("krea_normal_row_routes", smoke_krea_normal_row_routes),
("krea_action_details_policy", smoke_krea_action_details_policy),
("outercourse_action_policy", smoke_outercourse_action_policy),
("item_axis_policy", smoke_item_axis_policy),
("krea_row_fields_policy", smoke_krea_row_fields_policy),
("location_config_policy", smoke_location_config_policy),
("row_location_policy", smoke_row_location_policy),
("row_expression_policy", smoke_row_expression_policy),
("row_prompt_axes_policy", smoke_row_prompt_axes_policy),
("row_item_policy", smoke_row_item_policy),
("row_category_route_policy", smoke_row_category_route_policy),
("row_generation_policy", smoke_row_generation_policy),
("category_extensions_policy", smoke_category_extensions_policy),
("category_cast_config_policy", smoke_category_cast_config_policy),
("row_subject_route_policy", smoke_row_subject_route_policy),
("generation_profile_config_policy", smoke_generation_profile_config_policy),
("filter_config_policy", smoke_filter_config_policy),
("character_config_policy", smoke_character_config_policy),
("character_profile_policy", smoke_character_profile_policy),
("row_normalization_policy", smoke_row_normalization_policy),
("prompt_hygiene_policy", smoke_prompt_hygiene_policy),
("row_rendering_policy", smoke_row_rendering_policy),
("row_role_graph_policy", smoke_row_role_graph_policy),
("row_assembly_policy", smoke_row_assembly_policy),
("formatter_input_policy", smoke_formatter_input_policy),
("formatter_target_policy", smoke_formatter_target_policy),
("formatter_detail_policy", smoke_formatter_detail_policy),
("krea_format_route_policy", smoke_krea_format_route_policy),
("formatter_cast_policy", smoke_formatter_cast_policy),
("caption_policy", smoke_caption_policy),
("caption_format_route_policy", smoke_caption_format_route_policy),
("caption_text_policy", smoke_caption_text_policy),
("caption_metadata_routes", smoke_caption_metadata_routes),
("sdxl_presets_policy", smoke_sdxl_presets_policy),
("sdxl_format_route_policy", smoke_sdxl_format_route_policy),
("sdxl_tag_policy", smoke_sdxl_tag_policy),
("sdxl_tag_routes", smoke_sdxl_tag_routes),
("hardcore_position_config_policy", smoke_hardcore_position_config_policy),
("row_route_metadata_policy", smoke_row_route_metadata_policy),
("category_library_route", smoke_category_library_route),
("category_subcategory_matrix", smoke_category_subcategory_matrix),
("hardcore_category_routes", smoke_hardcore_category_routes),
("krea_close_foreplay_route", smoke_krea_close_foreplay_route),
("pair_options_policy", smoke_pair_options_policy),
("pair_route_policy", smoke_pair_route_policy),
("pair_builder_policy", smoke_pair_builder_policy),
("insta_pair_same_cast", smoke_insta_pair),
("krea_pair_clothing_state", smoke_krea_pair_clothing_state),
("krea_anal_axis_compatibility", smoke_krea_anal_axis_compatibility),
("insta_pair_pov_man", smoke_insta_pair_pov),
("insta_pair_camera_split", smoke_insta_pair_camera_split),
("pov_camera_scene", smoke_pov_camera_scene),
("krea_pov_penetration_route", smoke_krea_pov_penetration_route),
("pov_outercourse_position_routes", smoke_pov_outercourse_position_routes),
("pov_oral_position_routes", smoke_pov_oral_position_routes),
("pov_penetration_position_routes", smoke_pov_penetration_position_routes),
("pov_anal_position_routes", smoke_pov_anal_position_routes),
("double_front_back_route", smoke_double_front_back_route),
("climax_position_routes", smoke_climax_position_routes),
("interaction_role_graph_routes", smoke_interaction_role_graph_routes),
("fallback_role_graph_routes", smoke_fallback_role_graph_routes),
("expression_disabled", smoke_no_expression_fallback),
("formatter_metadata_fixtures", smoke_formatter_metadata_fixtures),
("node_runtime_contracts", smoke_node_runtime_contracts),
("node_utility_registration", smoke_node_utility_registration),
("server_route_payload_policy", smoke_server_route_payload_policy),
("seed_config_policy", smoke_seed_config_policy),
("prompt_route_simulation_policy", smoke_prompt_route_simulation_policy),
("node_camera_registration", smoke_node_camera_registration),
("node_route_config_registration", smoke_node_route_config_registration),
("node_character_registration", smoke_node_character_registration),
("node_hardcore_position_registration", smoke_node_hardcore_position_registration),
("node_formatter_registration", smoke_node_formatter_registration),
("node_insta_registration", smoke_node_insta_registration),
("node_scene_chain_registration", smoke_node_scene_chain_registration),
("node_builder_registration", smoke_node_builder_registration),
("node_profile_filter_registration", smoke_node_profile_filter_registration),
]
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--list",
action="store_true",
help="List available smoke case names and exit.",
)
parser.add_argument(
"--case",
choices=[name for name, _func in SMOKE_CASES],
action="append",
help="Run only the named smoke case. Can be passed multiple times.",
)
parser.add_argument(
"--quiet",
action="store_true",
help="Suppress passing case lines and print one success summary.",
)
args = parser.parse_args(argv)
if args.list:
for name, _func in SMOKE_CASES:
print(name)
return 0
selected = set(args.case or [])
report = SmokeReport(verbose=not args.quiet)
for name, func in SMOKE_CASES:
if selected and name not in selected:
continue
try:
func()
except Exception as exc: # noqa: BLE001 - report all smoke failures uniformly.
report.fail(name, str(exc))
else:
report.ok(name)
if args.quiet and not report.failed:
print(f"OK: smoke passed ({len(report.passed)} cases).")
else:
print(f"\nSummary: {len(report.passed)} passed, {len(report.failed)} failed")
if report.failed:
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())