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

1054 lines
46 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 re
import sys
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 krea_formatter # noqa: E402
import prompt_builder as pb # noqa: E402
import sdxl_formatter # 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 _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")
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,
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 _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 _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 _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 _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]:
location_config, composition_config, _summary = pb.build_thematic_location_json(
enabled=True,
combine_mode="replace",
theme="classical_library",
)
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,
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="",
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",
},
"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_trigger_once("builtin_single_woman.prompt", row.get("prompt"), Trigger)
_expect_formatter_outputs(row, "builtin_single_woman", target="single")
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")
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)
_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("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")
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("315-degree front-left quarter view" in prompt, "Krea config route lost camera directive")
_expect_formatter_outputs(row, "config_route_location_theme", 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}")
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 _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}.softcore_prompt", pair.get("softcore_prompt"), 20)
_expect_text(f"{name}.hardcore_prompt", pair.get("hardcore_prompt"), 20)
_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")
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")
def smoke_krea_pair_clothing_state() -> None:
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, "krea_pair_clothing_state")
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()
_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")
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")
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 tightly around", "glans sits just below"),
("press her breasts tightly around", "glans just below", "penis shaft"),
),
(
"pov_outercourse_testicle",
"testicle_sucking",
("mouth and tongue on the pov viewer's balls", "penis points upward"),
("mouth and tongue licking", "balls", "penis points upward"),
),
(
"pov_outercourse_penis_licking",
"penis_licking",
("head low under the pov viewer's penis", "tongue running along"),
("tongue runs along", "penis shaft", "glans"),
),
(
"pov_outercourse_handjob",
"handjob",
("one hand wrapped around the pov viewer's penis", "strokes toward the glans"),
("one hand wraps around", "penis shaft", "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}")
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_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}")
SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
("builtin_single_woman", smoke_builtin_single),
("camera_scene_single", smoke_camera_scene_single),
("config_route_location_theme", smoke_config_route_location_theme),
("hardcore_category_routes", smoke_hardcore_category_routes),
("krea_close_foreplay_route", smoke_krea_close_foreplay_route),
("insta_pair_same_cast", smoke_insta_pair),
("krea_pair_clothing_state", smoke_krea_pair_clothing_state),
("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),
("expression_disabled", smoke_no_expression_fallback),
("formatter_metadata_fixtures", smoke_formatter_metadata_fixtures),
]
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())