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

8326 lines
408 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 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
Trigger = "sxcppnl7"
SdxlTrigger = "mythp0rt"
@dataclass
class SmokeReport:
passed: list[str] = field(default_factory=list)
failed: list[str] = field(default_factory=list)
def ok(self, name: str) -> None:
self.passed.append(name)
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 = "",
) -> 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,
)
_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")
_expect(body_phrase in str(krea_metadata.get("krea_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_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",
)
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",
"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)")
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)")
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_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("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")
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")
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",
)
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.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": "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",
)
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.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)",
)
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")
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",
)
_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.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.softcore_pair_tags is sdxl_tag_policy.softcore_pair_tags, "SDXL route deps lost softcore pair tag policy")
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")
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")
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}")
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}")
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"}) == "foreplay",
"Template action-family normalizer should accept subcategory-style 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")
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")
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": "foreplay",
"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") == "foreplay", "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": "foreplay",
"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"] == "foreplay", "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", {"foreplay", "outercourse"}, "manual stimulation", "manual 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}")
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")
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")
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("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")
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("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")
_expect("composition. explicit" in lower, "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(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"),
("takes the viewer's penis in her mouth", "viewer stands over her"),
),
(
"pov_oral_face_sitting",
"face_sitting",
("straddling the viewer's face", "pussy directly over the viewer's mouth"),
("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"),
("head-to-hips", "viewer's mouth on the woman's pussy"),
),
(
"pov_oral_edge_supported",
"edge_supported",
("raised edge with thighs open", "viewer kneels between her legs"),
("raised edge with thighs open", "viewer kneels between her legs"),
),
(
"pov_oral_side_lying",
"side_lying",
("woman a lies on her side", "viewer lies beside her hips"),
("woman lies on her side", "viewer lies beside her hips"),
),
(
"pov_oral_chair",
"chair_oral",
("viewer sits in a chair", "kneels between his thighs"),
("viewer sits in a chair", "kneels between the viewer's thighs"),
),
]
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")
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(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("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)
_expect(row.get("action_family") == "foreplay", f"{name} action_family should stay formatter foreplay")
_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')}")
_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="foreplay",
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")
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")
_expect(
node_tooltips._tooltip_for_input("SxCPSeedControl", "category_seed_mode")
== "auto/follow_main follows the main seed; fixed uses category_seed; random rerolls this axis each queue.",
"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")
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_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",
"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")
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_inputs = woman_slot_node.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")
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_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),
("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),
("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),
("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_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(
"--case",
choices=[name for name, _func in SMOKE_CASES],
action="append",
help="Run only the named smoke case. Can be passed multiple times.",
)
args = parser.parse_args(argv)
selected = set(args.case or [])
report = SmokeReport()
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)
print(f"\nSummary: {len(report.passed)} passed, {len(report.failed)} failed")
if report.failed:
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())