1307 lines
57 KiB
Python
1307 lines
57 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 _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 _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 _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]:
|
|
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_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_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),
|
|
("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),
|
|
("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())
|