Extract interaction role graph wording
This commit is contained in:
@@ -102,6 +102,9 @@ Already isolated:
|
||||
- hardcore configured-cast role graph generation lives in
|
||||
`hardcore_role_graphs.py`; `prompt_builder.py` selects item/axis metadata and
|
||||
then asks that module for the source role graph.
|
||||
- interaction-style role graph wording lives in `hardcore_role_interaction.py`,
|
||||
covering foreplay, manual stimulation, body worship, clothing transitions,
|
||||
dominant guidance, camera performance, aftercare, and group coordination.
|
||||
- outercourse-specific role graph wording has started moving into action-family
|
||||
modules; `hardcore_role_outercourse.py` owns boobjob, testicle-sucking,
|
||||
penis-licking, handjob, and footjob body geometry.
|
||||
@@ -318,6 +321,8 @@ Near-term:
|
||||
Krea routes so selected position geometry stays synchronized with metadata.
|
||||
- Cover generated climax routes through Krea, SDXL, and natural caption outputs
|
||||
so source aftermath placement and formatter details cannot drift apart.
|
||||
- Cover generated interaction routes through Krea, SDXL, and natural caption
|
||||
outputs so source contact/guidance/presentation wording stays metadata-driven.
|
||||
|
||||
Medium-term:
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ Core helper ownership:
|
||||
| Python module | What it owns |
|
||||
| --- | --- |
|
||||
| `hardcore_role_graphs.py` | Source role graph construction for hardcore configured-cast rows, including POV-aware interaction geometry. |
|
||||
| `hardcore_role_interaction.py` | Foreplay, manual stimulation, body worship, clothing transition, dominant guidance, camera performance, aftercare, and group coordination role graph wording. |
|
||||
| `hardcore_role_oral.py` | Oral-sex role graph wording for kneeling, face-sitting, sixty-nine, edge-supported, side-lying, chair, standing, and reclining oral geometry. |
|
||||
| `hardcore_role_outercourse.py` | Outercourse role graph wording for boobjob, testicle-sucking, penis-licking, handjob, and footjob geometry. |
|
||||
| `hardcore_role_penetration.py` | Penetrative-sex role graph wording for missionary, cowgirl, reverse-cowgirl, doggy, standing, side-lying, raised-edge, kneeling-straddle, and lotus geometry. |
|
||||
@@ -752,6 +753,9 @@ pair metadata through the core Python APIs, then verifies:
|
||||
- climax routes keep source body position, Krea aftermath wording, SDXL family
|
||||
tags, and training captions synchronized for face-down, side-lying, lap,
|
||||
open-thigh, and front/back placements;
|
||||
- interaction routes keep source role graphs, Krea prose, SDXL tags, and
|
||||
training captions synchronized for manual, clothing transition, body worship,
|
||||
camera-performance, aftercare, and group-coordination rows;
|
||||
- expression-disabled rows do not fall back to generated expression text.
|
||||
- static formatter metadata fixtures keep source-provided action families
|
||||
stable across Krea2 prose, SDXL tags, and natural captions even when raw item
|
||||
|
||||
+29
-111
@@ -6,12 +6,24 @@ from typing import Any
|
||||
try:
|
||||
from .hardcore_role_anal import build_anal_or_double_role_graph
|
||||
from .hardcore_role_climax import build_climax_role_graph
|
||||
from .hardcore_role_interaction import (
|
||||
build_foreplay_role_graph,
|
||||
build_group_coordination_role_graph,
|
||||
build_interaction_role_graph,
|
||||
build_manual_role_graph,
|
||||
)
|
||||
from .hardcore_role_oral import build_oral_role_graph
|
||||
from .hardcore_role_outercourse import build_outercourse_role_graph
|
||||
from .hardcore_role_penetration import build_penetration_role_graph
|
||||
except ImportError: # Allows local smoke tests with `python -c`.
|
||||
from hardcore_role_anal import build_anal_or_double_role_graph
|
||||
from hardcore_role_climax import build_climax_role_graph
|
||||
from hardcore_role_interaction import (
|
||||
build_foreplay_role_graph,
|
||||
build_group_coordination_role_graph,
|
||||
build_interaction_role_graph,
|
||||
build_manual_role_graph,
|
||||
)
|
||||
from hardcore_role_oral import build_oral_role_graph
|
||||
from hardcore_role_outercourse import build_outercourse_role_graph
|
||||
from hardcore_role_penetration import build_penetration_role_graph
|
||||
@@ -91,112 +103,11 @@ def build_hardcore_role_graph(
|
||||
]
|
||||
return f" {extra} {rng.choice(actions)}."
|
||||
|
||||
def foreplay_position_graph(primary: str, partner: str) -> str:
|
||||
text = " ".join(
|
||||
str(part or "").lower()
|
||||
for part in (
|
||||
item_text,
|
||||
*((item_axis_values or {}).values()),
|
||||
)
|
||||
)
|
||||
if any(term in text for term in ("undressing", "removing clothing", "removing clothes", "pulling clothing", "sliding straps", "unbuttoning")):
|
||||
return (
|
||||
f"{primary} and {partner} stand close while {partner}'s hands pull clothing aside from {primary}'s body; "
|
||||
f"{primary}'s exposed skin and the clothing being removed stay clearly visible."
|
||||
)
|
||||
if any(term in text for term in ("breast", "breasts", "nipple", "cupping breasts", "touching breasts")):
|
||||
return (
|
||||
f"{primary} and {partner} press their bodies close while {partner}'s hand cups {primary}'s breast; "
|
||||
f"their faces stay close and the breast-touching gesture is clear."
|
||||
)
|
||||
if any(term in text for term in ("face", "cheek", "jaw", "chin", "hand on the cheek", "fingers under the chin")):
|
||||
return (
|
||||
f"{primary} and {partner} stand face-to-face at close range while one hand holds {primary}'s cheek and jaw; "
|
||||
f"their lips are close and the face-touching gesture is clear."
|
||||
)
|
||||
if any(term in text for term in ("kiss", "kissing", "mouth-to-mouth", "lips pressed")):
|
||||
return (
|
||||
f"{primary} and {partner} press their bodies together and kiss deeply, "
|
||||
f"with hands on each other's face, waist, and hips."
|
||||
)
|
||||
return (
|
||||
f"{primary} and {partner} are pressed close in a heated foreplay setup, "
|
||||
f"hands caressing skin while clothing is pulled aside."
|
||||
)
|
||||
|
||||
def interaction_text() -> str:
|
||||
return " ".join(
|
||||
str(part or "").lower()
|
||||
for part in (
|
||||
item_text,
|
||||
*((item_axis_values or {}).values()),
|
||||
)
|
||||
)
|
||||
|
||||
def manual_position_graph(primary: str, partner: str = "") -> str:
|
||||
text = interaction_text()
|
||||
if not partner:
|
||||
if "mutual" in text:
|
||||
return f"{primary} faces the camera with thighs open, both hands on her body for solo mutual-style masturbation framing."
|
||||
return f"{primary} reclines with thighs open, one hand between her legs and fingers visibly stimulating her pussy."
|
||||
if "mutual" in text:
|
||||
return f"{primary} and {partner} sit close facing each other, both touching themselves while keeping hands, faces, and bodies visible."
|
||||
if "clit" in text or "clitoris" in text:
|
||||
return f"{primary} reclines with thighs open while {partner}'s hand is between her legs, fingers rubbing her clit as her hips tilt toward the touch."
|
||||
if "toy" in text or "vibrator" in text:
|
||||
return f"{primary} reclines with thighs open while {partner} holds a vibrator or toy against her clit, one hand keeping her thigh open."
|
||||
return f"{primary} reclines with thighs open while {partner}'s hand is between her legs, fingers visibly stimulating her pussy."
|
||||
|
||||
def interaction_position_graph(primary: str, partner: str, third: str = "") -> str:
|
||||
text = interaction_text()
|
||||
if "aftercare" in slug or any(term in text for term in ("aftercare", "cleanup", "wiping", "towel", "post-sex", "cuddle")):
|
||||
if "cleanup" in text or "wiping" in text or "towel" in text:
|
||||
return f"{primary} reclines after sex while {partner} kneels close and wipes her skin with a towel, hands and relaxed body contact visible."
|
||||
return f"{primary} and {partner} lie close together after sex, bodies relaxed and hands resting on skin in a post-sex cuddle."
|
||||
if "camera_performance" in slug or any(term in text for term in ("camera", "presenting", "showing", "viewer", "creator-shot")):
|
||||
if third:
|
||||
return f"{primary} faces the camera while {partner} and {third} hold and present her body, hands framing the exposed skin for the viewer."
|
||||
return f"{primary} faces the camera and presents her body while {partner}'s hands hold her hips or thighs open for a clear creator-shot reveal."
|
||||
if "body_worship" in slug or any(term in text for term in ("body worship", "nipple", "thigh", "mouth on skin", "kissing down", "ass grabbing")):
|
||||
if "ass" in text:
|
||||
return f"{primary} stands or kneels with hips angled back while {partner}'s hands grip her ass, fingers pressing into skin."
|
||||
if "thigh" in text:
|
||||
return f"{primary} reclines with thighs open while {partner} kneels close and kisses along her inner thighs, hands holding her legs in place."
|
||||
if "nipple" in text or "breast" in text:
|
||||
return f"{primary} arches toward {partner} while {partner}'s mouth is on her breast and one hand cups or squeezes the other breast."
|
||||
return f"{primary} reclines or leans back while {partner} kisses down her body, hands tracing breasts, waist, hips, and thighs."
|
||||
if "clothing_position" in slug or any(term in text for term in ("transition", "turning", "pulling onto", "lifting", "guided backward", "clothing", "garment")):
|
||||
if "turn" in text or "rear-facing" in text:
|
||||
return f"{partner}'s hands turn {primary} around by the hips, clothing partly moved aside as her body rotates into the next pose."
|
||||
if "legs" in text or "thigh" in text:
|
||||
return f"{primary} lies back while {partner} lifts and spreads her legs into position, hands and clothing movement clearly visible."
|
||||
return f"{primary} and {partner} are mid-transition, with {partner}'s hands moving clothing aside and guiding {primary}'s hips toward the next pose."
|
||||
if "dominant" in slug or any(term in text for term in ("hair", "wrist", "wrists", "jaw", "chin", "guided", "dominant", "control", "dirty talk", "whisper", "mouth near the ear", "verbal teasing")):
|
||||
if "dirty talk" in text or "whisper" in text or "mouth near the ear" in text or "verbal teasing" in text:
|
||||
return f"{partner} leans close to {primary}'s ear for dirty talk while holding her waist and keeping their bodies pressed close."
|
||||
if "wrist" in text or "wrists" in text:
|
||||
return f"{primary} lies back while {partner} pins her wrists above her head, both bodies close and the consensual control gesture clearly visible."
|
||||
if "hair" in text:
|
||||
return f"{partner} holds {primary}'s hair back while guiding her body closer, face and hair-hold gesture visible."
|
||||
if "thigh" in text or "spread" in text:
|
||||
return f"{primary} reclines with thighs open while {partner}'s hands spread her legs and hold the position for the camera."
|
||||
return f"{partner} guides {primary}'s body with hands on her jaw, waist, and hips, keeping the consensual control gesture readable."
|
||||
return foreplay_position_graph(primary, partner)
|
||||
|
||||
def group_coordination_graph(primary: str, partner: str, third: str) -> str:
|
||||
observer = third or any_person({primary, partner})
|
||||
text = interaction_text()
|
||||
if "camera" in text or "hold" in text or "present" in text:
|
||||
return f"{primary} is centered while {partner} and {observer} hold and present the body for the camera, each role clearly visible."
|
||||
if "watch" in text or "waiting" in text:
|
||||
return f"{primary} is centered while {partner} touches her body and {observer} watches close beside them, hands and faces readable."
|
||||
return f"{primary} is centered while {partner} touches her body and {observer} stays close as the watching or guiding partner."
|
||||
|
||||
if people_count == 1:
|
||||
solo = people[0]
|
||||
if women_count == 1:
|
||||
if "manual_stimulation" in slug:
|
||||
return manual_position_graph(solo)
|
||||
return build_manual_role_graph(solo, item_text=item_text, item_axis_values=item_axis_values)
|
||||
if "camera_performance" in slug:
|
||||
return f"{solo} faces the camera and presents her body with hands framing the exposed skin in a solo creator-shot pose."
|
||||
if "cumshot" in slug or "climax" in slug:
|
||||
@@ -211,16 +122,16 @@ def build_hardcore_role_graph(
|
||||
c = any_woman({a, b}) if len(women) >= 3 else ""
|
||||
used = {a, b}
|
||||
if "manual_stimulation" in slug:
|
||||
graph = manual_position_graph(a, b)
|
||||
graph = build_manual_role_graph(a, b, item_text, item_axis_values)
|
||||
elif "group_coordination" in slug and c:
|
||||
graph = group_coordination_graph(a, b, c)
|
||||
graph = build_group_coordination_role_graph(a, b, c, item_text=item_text, item_axis_values=item_axis_values)
|
||||
used.add(c)
|
||||
elif any(token in slug for token in ("foreplay", "body_worship", "clothing_position", "dominant_guidance", "camera_performance", "aftercare")):
|
||||
graph = interaction_position_graph(a, b, c)
|
||||
graph = build_interaction_role_graph(a, b, c, slug, item_text, item_axis_values)
|
||||
if c and "camera_performance" in slug:
|
||||
used.add(c)
|
||||
elif "foreplay" in slug:
|
||||
graph = foreplay_position_graph(a, b)
|
||||
graph = build_foreplay_role_graph(a, b, item_text, item_axis_values)
|
||||
elif "outercourse" in slug:
|
||||
graph = f"{a} kneels close to {b}'s body and uses mouth, hands, breasts, or feet for explicit non-penetrative contact."
|
||||
elif "oral" in slug:
|
||||
@@ -244,7 +155,7 @@ def build_hardcore_role_graph(
|
||||
if "manual_stimulation" in slug:
|
||||
graph = f"{a} and {b} sit or recline close together with hands visibly stimulating bodies in a manual sex setup."
|
||||
elif "group_coordination" in slug and c:
|
||||
graph = group_coordination_graph(a, b, c)
|
||||
graph = build_group_coordination_role_graph(a, b, c, item_text=item_text, item_axis_values=item_axis_values)
|
||||
used.add(c)
|
||||
elif any(token in slug for token in ("foreplay", "body_worship", "clothing_position", "dominant_guidance", "camera_performance", "aftercare")):
|
||||
graph = f"{a} and {b} press close together, kissing and caressing skin while clothing is pulled aside."
|
||||
@@ -270,13 +181,20 @@ def build_hardcore_role_graph(
|
||||
man = any_man()
|
||||
third = any_person({woman, man}) if people_count >= 3 else ""
|
||||
if "manual_stimulation" in slug:
|
||||
graph = manual_position_graph(woman, man)
|
||||
graph = build_manual_role_graph(woman, man, item_text, item_axis_values)
|
||||
elif "group_coordination" in slug:
|
||||
graph = group_coordination_graph(woman, man, third)
|
||||
graph = build_group_coordination_role_graph(
|
||||
woman,
|
||||
man,
|
||||
third,
|
||||
any_person({woman, man}) if not third else "",
|
||||
item_text,
|
||||
item_axis_values,
|
||||
)
|
||||
elif any(token in slug for token in ("foreplay", "body_worship", "clothing_position", "dominant_guidance", "camera_performance", "aftercare")):
|
||||
graph = interaction_position_graph(woman, man, third)
|
||||
graph = build_interaction_role_graph(woman, man, third, slug, item_text, item_axis_values)
|
||||
elif "foreplay" in slug:
|
||||
graph = foreplay_position_graph(woman, man)
|
||||
graph = build_foreplay_role_graph(woman, man, item_text, item_axis_values)
|
||||
elif "outercourse" in slug:
|
||||
graph = build_outercourse_role_graph(woman, man, item_text, item_axis_values, pov_labels)
|
||||
elif "oral" in slug:
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _context_text(item_text: str, item_axis_values: dict[str, Any] | None) -> str:
|
||||
return " ".join(
|
||||
str(part or "").lower()
|
||||
for part in (
|
||||
item_text,
|
||||
*((item_axis_values or {}).values()),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def build_foreplay_role_graph(
|
||||
primary: str,
|
||||
partner: str,
|
||||
item_text: str,
|
||||
item_axis_values: dict[str, Any] | None = None,
|
||||
) -> str:
|
||||
text = _context_text(item_text, item_axis_values)
|
||||
if any(term in text for term in ("undressing", "removing clothing", "removing clothes", "pulling clothing", "sliding straps", "unbuttoning")):
|
||||
return (
|
||||
f"{primary} and {partner} stand close while {partner}'s hands pull clothing aside from {primary}'s body; "
|
||||
f"{primary}'s exposed skin and the clothing being removed stay clearly visible."
|
||||
)
|
||||
if any(term in text for term in ("breast", "breasts", "nipple", "cupping breasts", "touching breasts")):
|
||||
return (
|
||||
f"{primary} and {partner} press their bodies close while {partner}'s hand cups {primary}'s breast; "
|
||||
f"their faces stay close and the breast-touching gesture is clear."
|
||||
)
|
||||
if any(term in text for term in ("face", "cheek", "jaw", "chin", "hand on the cheek", "fingers under the chin")):
|
||||
return (
|
||||
f"{primary} and {partner} stand face-to-face at close range while one hand holds {primary}'s cheek and jaw; "
|
||||
f"their lips are close and the face-touching gesture is clear."
|
||||
)
|
||||
if any(term in text for term in ("kiss", "kissing", "mouth-to-mouth", "lips pressed")):
|
||||
return (
|
||||
f"{primary} and {partner} press their bodies together and kiss deeply, "
|
||||
f"with hands on each other's face, waist, and hips."
|
||||
)
|
||||
return (
|
||||
f"{primary} and {partner} are pressed close in a heated foreplay setup, "
|
||||
f"hands caressing skin while clothing is pulled aside."
|
||||
)
|
||||
|
||||
|
||||
def build_manual_role_graph(
|
||||
primary: str,
|
||||
partner: str = "",
|
||||
item_text: str = "",
|
||||
item_axis_values: dict[str, Any] | None = None,
|
||||
) -> str:
|
||||
text = _context_text(item_text, item_axis_values)
|
||||
if not partner:
|
||||
if "mutual" in text:
|
||||
return f"{primary} faces the camera with thighs open, both hands on her body for solo mutual-style masturbation framing."
|
||||
return f"{primary} reclines with thighs open, one hand between her legs and fingers visibly stimulating her pussy."
|
||||
if "mutual" in text:
|
||||
return f"{primary} and {partner} sit close facing each other, both touching themselves while keeping hands, faces, and bodies visible."
|
||||
if "clit" in text or "clitoris" in text:
|
||||
return f"{primary} reclines with thighs open while {partner}'s hand is between her legs, fingers rubbing her clit as her hips tilt toward the touch."
|
||||
if "toy" in text or "vibrator" in text:
|
||||
return f"{primary} reclines with thighs open while {partner} holds a vibrator or toy against her clit, one hand keeping her thigh open."
|
||||
return f"{primary} reclines with thighs open while {partner}'s hand is between her legs, fingers visibly stimulating her pussy."
|
||||
|
||||
|
||||
def build_interaction_role_graph(
|
||||
primary: str,
|
||||
partner: str,
|
||||
third: str = "",
|
||||
slug: str = "",
|
||||
item_text: str = "",
|
||||
item_axis_values: dict[str, Any] | None = None,
|
||||
) -> str:
|
||||
text = _context_text(item_text, item_axis_values)
|
||||
if "aftercare" in slug or any(term in text for term in ("aftercare", "cleanup", "wiping", "towel", "post-sex", "cuddle")):
|
||||
if "cleanup" in text or "wiping" in text or "towel" in text:
|
||||
return f"{primary} reclines after sex while {partner} kneels close and wipes her skin with a towel, hands and relaxed body contact visible."
|
||||
return f"{primary} and {partner} lie close together after sex, bodies relaxed and hands resting on skin in a post-sex cuddle."
|
||||
if "camera_performance" in slug or any(term in text for term in ("camera", "presenting", "showing", "viewer", "creator-shot")):
|
||||
if third:
|
||||
return f"{primary} faces the camera while {partner} and {third} hold and present her body, hands framing the exposed skin for the viewer."
|
||||
return f"{primary} faces the camera and presents her body while {partner}'s hands hold her hips or thighs open for a clear creator-shot reveal."
|
||||
if "body_worship" in slug or any(term in text for term in ("body worship", "nipple", "thigh", "mouth on skin", "kissing down", "ass grabbing")):
|
||||
if "ass" in text:
|
||||
return f"{primary} stands or kneels with hips angled back while {partner}'s hands grip her ass, fingers pressing into skin."
|
||||
if "thigh" in text:
|
||||
return f"{primary} reclines with thighs open while {partner} kneels close and kisses along her inner thighs, hands holding her legs in place."
|
||||
if "nipple" in text or "breast" in text:
|
||||
return f"{primary} arches toward {partner} while {partner}'s mouth is on her breast and one hand cups or squeezes the other breast."
|
||||
return f"{primary} reclines or leans back while {partner} kisses down her body, hands tracing breasts, waist, hips, and thighs."
|
||||
if "clothing_position" in slug or any(term in text for term in ("transition", "turning", "pulling onto", "lifting", "guided backward", "clothing", "garment")):
|
||||
if "turn" in text or "rear-facing" in text:
|
||||
return f"{partner}'s hands turn {primary} around by the hips, clothing partly moved aside as her body rotates into the next pose."
|
||||
if "legs" in text or "thigh" in text:
|
||||
return f"{primary} lies back while {partner} lifts and spreads her legs into position, hands and clothing movement clearly visible."
|
||||
return f"{primary} and {partner} are mid-transition, with {partner}'s hands moving clothing aside and guiding {primary}'s hips toward the next pose."
|
||||
if "dominant" in slug or any(term in text for term in ("hair", "wrist", "wrists", "jaw", "chin", "guided", "dominant", "control", "dirty talk", "whisper", "mouth near the ear", "verbal teasing")):
|
||||
if "dirty talk" in text or "whisper" in text or "mouth near the ear" in text or "verbal teasing" in text:
|
||||
return f"{partner} leans close to {primary}'s ear for dirty talk while holding her waist and keeping their bodies pressed close."
|
||||
if "wrist" in text or "wrists" in text:
|
||||
return f"{primary} lies back while {partner} pins her wrists above her head, both bodies close and the consensual control gesture clearly visible."
|
||||
if "hair" in text:
|
||||
return f"{partner} holds {primary}'s hair back while guiding her body closer, face and hair-hold gesture visible."
|
||||
if "thigh" in text or "spread" in text:
|
||||
return f"{primary} reclines with thighs open while {partner}'s hands spread her legs and hold the position for the camera."
|
||||
return f"{partner} guides {primary}'s body with hands on her jaw, waist, and hips, keeping the consensual control gesture readable."
|
||||
return build_foreplay_role_graph(primary, partner, item_text, item_axis_values)
|
||||
|
||||
|
||||
def build_group_coordination_role_graph(
|
||||
primary: str,
|
||||
partner: str,
|
||||
third: str = "",
|
||||
fallback_observer: str = "",
|
||||
item_text: str = "",
|
||||
item_axis_values: dict[str, Any] | None = None,
|
||||
) -> str:
|
||||
observer = third or fallback_observer or partner
|
||||
text = _context_text(item_text, item_axis_values)
|
||||
if "camera" in text or "hold" in text or "present" in text:
|
||||
return f"{primary} is centered while {partner} and {observer} hold and present the body for the camera, each role clearly visible."
|
||||
if "watch" in text or "waiting" in text:
|
||||
return f"{primary} is centered while {partner} touches her body and {observer} watches close beside them, hands and faces readable."
|
||||
return f"{primary} is centered while {partner} touches her body and {observer} stays close as the watching or guiding partner."
|
||||
@@ -1217,6 +1217,114 @@ def smoke_climax_position_routes() -> None:
|
||||
_expect_formatter_outputs(row, name, target="single")
|
||||
|
||||
|
||||
def smoke_interaction_role_graph_routes() -> None:
|
||||
cases = [
|
||||
(
|
||||
"interaction_manual",
|
||||
"Manual stimulation",
|
||||
"manual_only",
|
||||
"manual",
|
||||
"fingering",
|
||||
4301,
|
||||
_character_cast(),
|
||||
1,
|
||||
1,
|
||||
("reclines with thighs open", "fingers visibly stimulating"),
|
||||
("fingers visibly stimulating", "between her legs"),
|
||||
),
|
||||
(
|
||||
"interaction_clothing_transition",
|
||||
"Clothing and position transitions",
|
||||
"interaction_only",
|
||||
"interaction",
|
||||
"position_transition",
|
||||
4302,
|
||||
_character_cast(),
|
||||
1,
|
||||
1,
|
||||
("mid-transition", "moving clothing aside"),
|
||||
("mid-transition", "guiding the woman's hips"),
|
||||
),
|
||||
(
|
||||
"interaction_body_worship",
|
||||
"Body worship and touching",
|
||||
"interaction_only",
|
||||
"interaction",
|
||||
"body_worship",
|
||||
4301,
|
||||
_character_cast(),
|
||||
1,
|
||||
1,
|
||||
("kisses down her body", "hands tracing"),
|
||||
("kisses down her body", "hands tracing"),
|
||||
),
|
||||
(
|
||||
"interaction_camera_performance",
|
||||
"Camera performance",
|
||||
"interaction_only",
|
||||
"interaction",
|
||||
"camera_showing",
|
||||
4301,
|
||||
_character_cast(),
|
||||
1,
|
||||
1,
|
||||
("faces the camera", "creator-shot reveal"),
|
||||
("faces the camera", "creator-shot reveal"),
|
||||
),
|
||||
(
|
||||
"interaction_aftercare",
|
||||
"Aftercare and cleanup",
|
||||
"interaction_only",
|
||||
"interaction",
|
||||
"aftercare",
|
||||
4301,
|
||||
_character_cast(),
|
||||
1,
|
||||
1,
|
||||
("lie close together after sex", "post-sex cuddle"),
|
||||
("lie close together after sex", "post-sex cuddle"),
|
||||
),
|
||||
(
|
||||
"interaction_group_coordination",
|
||||
"Group coordination",
|
||||
"interaction_only",
|
||||
"interaction",
|
||||
"watching",
|
||||
4301,
|
||||
_character_cast_two_men(),
|
||||
1,
|
||||
2,
|
||||
("is centered", "hold and present"),
|
||||
("is centered", "each role clearly visible"),
|
||||
),
|
||||
]
|
||||
for name, subcategory, focus, family, position_key, seed, cast, women_count, men_count, role_terms, krea_terms in cases:
|
||||
row = _prompt_row(
|
||||
name=name,
|
||||
category="Hardcore sexual poses",
|
||||
subcategory=subcategory,
|
||||
seed=seed,
|
||||
character_cast=cast,
|
||||
women_count=women_count,
|
||||
men_count=men_count,
|
||||
hardcore_position_config=_position_filter(focus, family, [position_key]),
|
||||
)
|
||||
_expect_custom_row(row, name)
|
||||
_expect(row.get("action_family") == "foreplay", f"{name} action_family should stay formatter foreplay")
|
||||
_expect(row.get("position_family") == family, f"{name} position_family mismatch: {row.get('position_family')}")
|
||||
_expect(position_key in (row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
|
||||
role_graph = _expect_text(f"{name}.source_role_graph", row.get("source_role_graph"), 40).lower()
|
||||
for term in role_terms:
|
||||
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
|
||||
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
|
||||
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
|
||||
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
|
||||
_expect("role graph:" not in prompt and "sexual scene:" not in prompt, f"{name} Krea leaked raw labels")
|
||||
for term in krea_terms:
|
||||
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
|
||||
_expect_formatter_outputs(row, name, target="single")
|
||||
|
||||
|
||||
def smoke_no_expression_fallback() -> None:
|
||||
cast = pb.build_character_slot_json(
|
||||
subject_type="woman",
|
||||
@@ -1354,6 +1462,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
|
||||
("pov_anal_position_routes", smoke_pov_anal_position_routes),
|
||||
("double_front_back_route", smoke_double_front_back_route),
|
||||
("climax_position_routes", smoke_climax_position_routes),
|
||||
("interaction_role_graph_routes", smoke_interaction_role_graph_routes),
|
||||
("expression_disabled", smoke_no_expression_fallback),
|
||||
("formatter_metadata_fixtures", smoke_formatter_metadata_fixtures),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user