Extract anal role graph wording
This commit is contained in:
@@ -111,6 +111,9 @@ Already isolated:
|
|||||||
- penetration-specific role graph wording lives in
|
- penetration-specific role graph wording lives in
|
||||||
`hardcore_role_penetration.py`, covering the main vaginal penetration
|
`hardcore_role_penetration.py`, covering the main vaginal penetration
|
||||||
position families while Krea POV rewriting keeps first-person geometry stable.
|
position families while Krea POV rewriting keeps first-person geometry stable.
|
||||||
|
- anal/double-contact role graph wording lives in `hardcore_role_anal.py`,
|
||||||
|
covering rear-entry anal variants and front/back double-contact source
|
||||||
|
geometry.
|
||||||
- camera-scene prose and coworking composition adaptation live in
|
- camera-scene prose and coworking composition adaptation live in
|
||||||
`scene_camera_adapters.py`; `prompt_builder.py` still owns camera config
|
`scene_camera_adapters.py`; `prompt_builder.py` still owns camera config
|
||||||
parsing and row mutation.
|
parsing and row mutation.
|
||||||
@@ -177,8 +180,9 @@ Already isolated:
|
|||||||
- `krea_action_dispatch.py` owns non-POV role normalization, action-family
|
- `krea_action_dispatch.py` owns non-POV role normalization, action-family
|
||||||
classification, and family-specific detail cleanup.
|
classification, and family-specific detail cleanup.
|
||||||
- `krea_actions.py` owns final non-POV hardcore action sentence assembly.
|
- `krea_actions.py` owns final non-POV hardcore action sentence assembly.
|
||||||
- `krea_pov_actions.py` owns POV hardcore action sentence rewriting and
|
- `krea_pov_actions.py` owns POV hardcore action sentence rewriting,
|
||||||
first-person body geometry.
|
first-person body geometry, and selected-position-axis priority before loose
|
||||||
|
context fallback.
|
||||||
|
|
||||||
Improve later:
|
Improve later:
|
||||||
|
|
||||||
@@ -307,6 +311,8 @@ Near-term:
|
|||||||
- Cover close foreplay and POV penetration Krea routes so raw labels, invalid
|
- Cover close foreplay and POV penetration Krea routes so raw labels, invalid
|
||||||
surface grammar, normal third-person camera text, and composition punctuation
|
surface grammar, normal third-person camera text, and composition punctuation
|
||||||
drift are caught.
|
drift are caught.
|
||||||
|
- Cover POV outercourse, oral, penetration, anal, and front/back double-contact
|
||||||
|
Krea routes so selected position geometry stays synchronized with metadata.
|
||||||
|
|
||||||
Medium-term:
|
Medium-term:
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ Core helper ownership:
|
|||||||
| `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_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_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. |
|
| `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. |
|
||||||
|
| `hardcore_role_anal.py` | Anal and double-contact role graph wording for rear-entry, raised-edge, kneeling, side-lying, and front/back double-position geometry. |
|
||||||
| `hardcore_action_metadata.py` | Source action-family and position-family metadata used by Krea2, SDXL, and caption routes. |
|
| `hardcore_action_metadata.py` | Source action-family and position-family metadata used by Krea2, SDXL, and caption routes. |
|
||||||
| `scene_camera_adapters.py` | Location-aware camera/scene prose such as coworking lounge camera layout. |
|
| `scene_camera_adapters.py` | Location-aware camera/scene prose such as coworking lounge camera layout. |
|
||||||
| `prompt_hygiene.py` | Generic prompt, caption, and negative-prompt cleanup. |
|
| `prompt_hygiene.py` | Generic prompt, caption, and negative-prompt cleanup. |
|
||||||
@@ -481,6 +482,8 @@ What each part owns:
|
|||||||
- `sexual_poses.json`: available positions, families, action templates, role
|
- `sexual_poses.json`: available positions, families, action templates, role
|
||||||
graph templates, interaction templates, and action-specific pool references.
|
graph templates, interaction templates, and action-specific pool references.
|
||||||
- `prompt_builder.py`: filters which templates/axes remain available.
|
- `prompt_builder.py`: filters which templates/axes remain available.
|
||||||
|
- `hardcore_role_graphs.py` and action-family helper modules: turn selected
|
||||||
|
item axes into source role graphs before formatter-specific rewrites.
|
||||||
- `krea_formatter.py`: orchestrates the selected action rewrite into
|
- `krea_formatter.py`: orchestrates the selected action rewrite into
|
||||||
model-readable prose.
|
model-readable prose.
|
||||||
- `krea_action_positions.py`: resolves non-POV pose anchors, body-arrangement
|
- `krea_action_positions.py`: resolves non-POV pose anchors, body-arrangement
|
||||||
@@ -740,6 +743,11 @@ pair metadata through the core Python APIs, then verifies:
|
|||||||
without recursive viewer wording;
|
without recursive viewer wording;
|
||||||
- POV penetration routes keep constrained missionary, cowgirl, reverse-cowgirl,
|
- POV penetration routes keep constrained missionary, cowgirl, reverse-cowgirl,
|
||||||
doggy, raised-edge, and lotus geometry through Krea formatting;
|
doggy, raised-edge, and lotus geometry through Krea formatting;
|
||||||
|
- POV anal routes keep constrained doggy, bent-over, face-down, standing,
|
||||||
|
side-lying, raised-edge, and kneeling rear-entry geometry through Krea
|
||||||
|
formatting;
|
||||||
|
- front/back double-contact routes keep source role graph metadata and Krea
|
||||||
|
front/back position wording synchronized;
|
||||||
- expression-disabled rows do not fall back to generated expression text.
|
- expression-disabled rows do not fall back to generated expression text.
|
||||||
- static formatter metadata fixtures keep source-provided action families
|
- static formatter metadata fixtures keep source-provided action families
|
||||||
stable across Krea2 prose, SDXL tags, and natural captions even when raw item
|
stable across Krea2 prose, SDXL tags, and natural captions even when raw item
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
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 _anal_position_graph(woman: str, man: str, context: str) -> str:
|
||||||
|
if "bent-over" in context or "bent over" in context:
|
||||||
|
return f"{woman} is bent forward with hips raised while {man} stands behind her and thrusts his penis into her ass."
|
||||||
|
if "face-down" in context:
|
||||||
|
return f"{woman} lies face-down with ass raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
if "doggy" in context or "rear-entry" in context:
|
||||||
|
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
if "standing" in context:
|
||||||
|
return f"{woman} stands braced with hips angled back while {man} stands behind her and thrusts his penis into her ass."
|
||||||
|
if "spooning" in context or "side-lying" in context:
|
||||||
|
return f"{woman} lies on her side with thighs parted while {man} presses behind her and thrusts his penis into her ass."
|
||||||
|
if "edge-of-bed" in context or "edge of bed" in context or "bed edge" in context or "edge-supported" in context:
|
||||||
|
return f"{woman} lies near a raised edge with hips exposed while {man} kneels behind her and thrusts his penis into her ass."
|
||||||
|
if "kneeling" in context:
|
||||||
|
return f"{woman} kneels forward with hips raised while {man} kneels behind her and thrusts his penis into her ass."
|
||||||
|
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
|
||||||
|
|
||||||
|
def _two_person_double_graph(woman: str, man: str, context: str) -> str:
|
||||||
|
if "bent-over" in context or "bent over" in context:
|
||||||
|
return f"{woman} is bent forward with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
if "face-down" in context:
|
||||||
|
return f"{woman} lies face-down with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
if "standing" in context:
|
||||||
|
return f"{woman} stands braced with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
if "kneeling" in context:
|
||||||
|
return f"{woman} kneels forward with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
|
||||||
|
|
||||||
|
def build_anal_or_double_role_graph(
|
||||||
|
woman: str,
|
||||||
|
man: str,
|
||||||
|
third: str,
|
||||||
|
people_count: int,
|
||||||
|
item_text: str,
|
||||||
|
item_axis_values: dict[str, Any] | None = None,
|
||||||
|
) -> str:
|
||||||
|
context = _context_text(item_text, item_axis_values)
|
||||||
|
if "double" in context or "toy" in context:
|
||||||
|
if people_count >= 3 and third:
|
||||||
|
return f"{man} thrusts his penis into {woman} while {third} adds a second penetration point from the front."
|
||||||
|
return _two_person_double_graph(woman, man, context)
|
||||||
|
if people_count >= 3 and third:
|
||||||
|
return f"{man} thrusts his penis into {woman} while {third} gives oral contact from the front."
|
||||||
|
return _anal_position_graph(woman, man, context)
|
||||||
+3
-42
@@ -5,10 +5,12 @@ import re
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
from .hardcore_role_anal import build_anal_or_double_role_graph
|
||||||
from .hardcore_role_oral import build_oral_role_graph
|
from .hardcore_role_oral import build_oral_role_graph
|
||||||
from .hardcore_role_outercourse import build_outercourse_role_graph
|
from .hardcore_role_outercourse import build_outercourse_role_graph
|
||||||
from .hardcore_role_penetration import build_penetration_role_graph
|
from .hardcore_role_penetration import build_penetration_role_graph
|
||||||
except ImportError: # Allows local smoke tests with `python -c`.
|
except ImportError: # Allows local smoke tests with `python -c`.
|
||||||
|
from hardcore_role_anal import build_anal_or_double_role_graph
|
||||||
from hardcore_role_oral import build_oral_role_graph
|
from hardcore_role_oral import build_oral_role_graph
|
||||||
from hardcore_role_outercourse import build_outercourse_role_graph
|
from hardcore_role_outercourse import build_outercourse_role_graph
|
||||||
from hardcore_role_penetration import build_penetration_role_graph
|
from hardcore_role_penetration import build_penetration_role_graph
|
||||||
@@ -240,30 +242,6 @@ def build_hardcore_role_graph(
|
|||||||
return f"{woman} kneels in front of {man} at hip height while {man} ejaculates semen onto her face, lips, and chest."
|
return f"{woman} kneels in front of {man} at hip height while {man} ejaculates semen onto her face, lips, and chest."
|
||||||
return f"{woman} lies on her back with thighs open while {man} kneels between her legs and ejaculates semen onto her body."
|
return f"{woman} lies on her back with thighs open while {man} kneels between her legs and ejaculates semen onto her body."
|
||||||
|
|
||||||
def anal_position_graph(woman: str, man: str) -> str:
|
|
||||||
text = " ".join(
|
|
||||||
str(part or "").lower()
|
|
||||||
for part in (
|
|
||||||
item_text,
|
|
||||||
*((item_axis_values or {}).values()),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if "bent-over" in text or "bent over" in text:
|
|
||||||
return f"{woman} is bent forward with hips raised while {man} stands behind her and thrusts his penis into her ass."
|
|
||||||
if "face-down" in text:
|
|
||||||
return f"{woman} lies face-down with ass raised while {man} is positioned behind her and thrusts his penis into her ass."
|
|
||||||
if "doggy" in text or "rear-entry" in text:
|
|
||||||
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
|
||||||
if "standing" in text:
|
|
||||||
return f"{woman} stands braced with hips angled back while {man} stands behind her and thrusts his penis into her ass."
|
|
||||||
if "spooning" in text or "side-lying" in text:
|
|
||||||
return f"{woman} lies on her side with thighs parted while {man} presses behind her and thrusts his penis into her ass."
|
|
||||||
if "edge-of-bed" in text or "edge of bed" in text or "bed edge" in text:
|
|
||||||
return f"{woman} lies near a raised edge with hips exposed while {man} kneels behind her and thrusts his penis into her ass."
|
|
||||||
if "kneeling" in text:
|
|
||||||
return f"{woman} kneels forward with hips raised while {man} kneels behind her and thrusts his penis into her ass."
|
|
||||||
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
|
||||||
|
|
||||||
if people_count == 1:
|
if people_count == 1:
|
||||||
solo = people[0]
|
solo = people[0]
|
||||||
if women_count == 1:
|
if women_count == 1:
|
||||||
@@ -354,24 +332,7 @@ def build_hardcore_role_graph(
|
|||||||
elif "oral" in slug:
|
elif "oral" in slug:
|
||||||
graph = build_oral_role_graph(woman, man, item_text, item_axis_values, pov_labels)
|
graph = build_oral_role_graph(woman, man, item_text, item_axis_values, pov_labels)
|
||||||
elif "anal" in slug or "double" in slug:
|
elif "anal" in slug or "double" in slug:
|
||||||
if "double" in item_text or "toy" in item_text:
|
graph = build_anal_or_double_role_graph(woman, man, third, people_count, item_text, item_axis_values)
|
||||||
if people_count >= 3:
|
|
||||||
graph = f"{man} thrusts his penis into {woman} while {third} adds a second penetration point from the front."
|
|
||||||
else:
|
|
||||||
if "bent-over" in item_text or "bent over" in item_text:
|
|
||||||
graph = f"{woman} is bent forward with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
|
||||||
elif "face-down" in item_text:
|
|
||||||
graph = f"{woman} lies face-down with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
|
||||||
elif "standing" in item_text:
|
|
||||||
graph = f"{woman} stands braced with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
|
||||||
elif "kneeling" in item_text:
|
|
||||||
graph = f"{woman} kneels forward with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
|
||||||
else:
|
|
||||||
graph = f"{woman} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
|
||||||
elif people_count >= 3:
|
|
||||||
graph = f"{man} thrusts his penis into {woman} while {third} gives oral contact from the front."
|
|
||||||
else:
|
|
||||||
graph = anal_position_graph(woman, man)
|
|
||||||
elif "threesome" in slug:
|
elif "threesome" in slug:
|
||||||
graph = f"{man} thrusts his penis into {woman} while {third or any_person({woman, man})} uses mouth and hands on the exposed body."
|
graph = f"{man} thrusts his penis into {woman} while {third or any_person({woman, man})} uses mouth and hands on the exposed body."
|
||||||
elif "group" in slug or "orgy" in slug:
|
elif "group" in slug or "orgy" in slug:
|
||||||
|
|||||||
+40
-31
@@ -138,6 +138,10 @@ def pov_hardcore_pose_sentence(
|
|||||||
action_lower = action_text.lower()
|
action_lower = action_text.lower()
|
||||||
if not context:
|
if not context:
|
||||||
context = action_lower
|
context = action_lower
|
||||||
|
position_text = ""
|
||||||
|
if isinstance(axis_values, dict):
|
||||||
|
position_text = _clean(axis_values.get("position", "")).lower()
|
||||||
|
position_context = position_text or context
|
||||||
|
|
||||||
def sentence(base: str) -> str:
|
def sentence(base: str) -> str:
|
||||||
details = ""
|
details = ""
|
||||||
@@ -212,63 +216,68 @@ def pov_hardcore_pose_sentence(
|
|||||||
|
|
||||||
contact = pov_contact_clause(action, role_graph, hard_item, axis_values, context)
|
contact = pov_contact_clause(action, role_graph, hard_item, axis_values, context)
|
||||||
|
|
||||||
if "reverse cowgirl" in context:
|
if "reverse cowgirl" in position_context:
|
||||||
return sentence(
|
return sentence(
|
||||||
"POV reverse cowgirl position: the viewer lies on his back while the woman straddles his hips facing away; "
|
"POV reverse cowgirl position: the viewer lies on his back while the woman straddles his hips facing away; "
|
||||||
f"her back, ass, thighs, and the viewer's foreground legs are visible {contact}"
|
f"her back, ass, thighs, and the viewer's foreground legs are visible {contact}"
|
||||||
)
|
)
|
||||||
if "cowgirl" in context or "straddling a partner" in context or "squatting on top" in context:
|
if "cowgirl" in position_context or "straddling a partner" in position_context or "squatting on top" in position_context:
|
||||||
return sentence(
|
return sentence(
|
||||||
"POV cowgirl position: the viewer lies on his back while the woman straddles his hips facing him; "
|
"POV cowgirl position: the viewer lies on his back while the woman straddles his hips facing him; "
|
||||||
f"her torso, hips, and open thighs fill the frame from below {contact}"
|
f"her torso, hips, and open thighs fill the frame from below {contact}"
|
||||||
)
|
)
|
||||||
if "lotus" in context or "seated in a partner's lap" in context:
|
if "lotus" in position_context or "seated in a partner's lap" in position_context:
|
||||||
return sentence(
|
return sentence(
|
||||||
"POV lotus position: the viewer sits upright while the woman sits in his lap facing him with her legs around his hips; "
|
"POV lotus position: the viewer sits upright while the woman sits in his lap facing him with her legs around his hips; "
|
||||||
f"her torso and hips stay close to the viewer {contact}"
|
f"her torso and hips stay close to the viewer {contact}"
|
||||||
)
|
)
|
||||||
if "kneeling straddle" in context:
|
if "kneeling straddle" in position_context:
|
||||||
return sentence(
|
return sentence(
|
||||||
"POV kneeling straddle position: the viewer kneels upright while the woman straddles his hips facing him; "
|
"POV kneeling straddle position: the viewer kneels upright while the woman straddles his hips facing him; "
|
||||||
f"both torsos are upright and her hips press directly against him {contact}"
|
f"both torsos are upright and her hips press directly against him {contact}"
|
||||||
)
|
)
|
||||||
if "face-down" in context or "face down" in context:
|
if "face-down" in position_context or "face down" in position_context:
|
||||||
return sentence(
|
return sentence(
|
||||||
"The woman is seen from behind with her ass raised toward the POV viewer, lying face-down with hips lifted; "
|
"The woman is seen from behind with her ass raised toward the POV viewer, lying face-down with hips lifted; "
|
||||||
f"the viewer looks down at her raised ass with foreground hands on her hips {contact}"
|
f"the viewer looks down at her raised ass with foreground hands on her hips {contact}"
|
||||||
)
|
)
|
||||||
if "bent-over" in context or "bent over" in context or "bent forward" in context:
|
|
||||||
return sentence(
|
|
||||||
"The woman is seen from behind with her ass raised toward the POV viewer, bent forward at the waist with hips lifted and head turned back; "
|
|
||||||
f"the viewer looks down at her raised ass from behind with foreground hands near her hips {contact}"
|
|
||||||
)
|
|
||||||
if "doggy" in context or "all fours" in context or "rear-entry" in context:
|
|
||||||
return sentence(
|
|
||||||
"The woman is seen from behind with her ass raised toward the POV viewer, on all fours directly in front of him with hips high and back arched; "
|
|
||||||
f"the viewer looks down at her raised ass with his hands on her hips in the foreground {contact}"
|
|
||||||
)
|
|
||||||
if "standing" in context:
|
|
||||||
return sentence(
|
|
||||||
"POV standing rear-entry position: the woman stands braced in front of the viewer with hips angled back and legs steady; "
|
|
||||||
f"the viewer stands behind her at hip level {contact}"
|
|
||||||
)
|
|
||||||
if "spooning" in context or "side-lying" in context or "lies on her side" in context:
|
|
||||||
return sentence(
|
|
||||||
"POV side-lying sex position: the woman lies on her side in front of the viewer with thighs parted; "
|
|
||||||
f"the viewer is behind her along the same body line {contact}"
|
|
||||||
)
|
|
||||||
if (
|
if (
|
||||||
"edge-supported" in context
|
"edge-supported" in position_context
|
||||||
or "raised edge" in context
|
or "raised edge" in position_context
|
||||||
or "edge of bed" in context
|
or "edge of bed" in position_context
|
||||||
or "bed edge" in context
|
or "bed edge" in position_context
|
||||||
or "kneels between her legs" in context
|
or (not position_text and "kneels between her legs" in context)
|
||||||
):
|
):
|
||||||
return sentence(
|
return sentence(
|
||||||
"POV raised-edge penetration position: the woman reclines at the raised edge with thighs open toward the viewer; "
|
"POV raised-edge penetration position: the woman reclines at the raised edge with thighs open toward the viewer; "
|
||||||
f"the viewer kneels between her legs with his hands near her hips {contact}"
|
f"the viewer kneels between her legs with his hands near her hips {contact}"
|
||||||
)
|
)
|
||||||
if "missionary" in context or ("lies on her back" in context and ("legs open" in context or "thighs open" in context)):
|
if "standing" in position_context:
|
||||||
|
return sentence(
|
||||||
|
"POV standing rear-entry position: the woman stands braced in front of the viewer with hips angled back and legs steady; "
|
||||||
|
f"the viewer stands behind her at hip level {contact}"
|
||||||
|
)
|
||||||
|
if "spooning" in position_context or "side-lying" in position_context or "lies on her side" in position_context:
|
||||||
|
return sentence(
|
||||||
|
"POV side-lying sex position: the woman lies on her side in front of the viewer with thighs parted; "
|
||||||
|
f"the viewer is behind her along the same body line {contact}"
|
||||||
|
)
|
||||||
|
if "doggy" in position_context or "all fours" in position_context or "rear-entry" in position_context:
|
||||||
|
return sentence(
|
||||||
|
"The woman is seen from behind with her ass raised toward the POV viewer, on all fours directly in front of him with hips high and back arched; "
|
||||||
|
f"the viewer looks down at her raised ass with his hands on her hips in the foreground {contact}"
|
||||||
|
)
|
||||||
|
if "kneeling" in position_context:
|
||||||
|
return sentence(
|
||||||
|
"POV kneeling rear-entry position: the woman kneels forward in front of the viewer with hips raised and thighs apart; "
|
||||||
|
f"the viewer kneels behind her at hip level with foreground hands near her waist {contact}"
|
||||||
|
)
|
||||||
|
if "bent-over" in position_context or "bent over" in position_context or "bent forward" in position_context:
|
||||||
|
return sentence(
|
||||||
|
"The woman is seen from behind with her ass raised toward the POV viewer, bent forward at the waist with hips lifted and head turned back; "
|
||||||
|
f"the viewer looks down at her raised ass from behind with foreground hands near her hips {contact}"
|
||||||
|
)
|
||||||
|
if "missionary" in position_context or (not position_text and "lies on her back" in context and ("legs open" in context or "thighs open" in context)):
|
||||||
return sentence(
|
return sentence(
|
||||||
"POV missionary position: the woman lies on her back with legs open around the viewer's hips; "
|
"POV missionary position: the woman lies on her back with legs open around the viewer's hips; "
|
||||||
f"the viewer is above her with foreground arms braced beside her body {contact}"
|
f"the viewer is above her with foreground arms braced beside her body {contact}"
|
||||||
|
|||||||
@@ -163,6 +163,23 @@ def _character_cast(*, pov_man: bool = False) -> str:
|
|||||||
)["character_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:
|
def _action_filter(focus: str, hardcore_position_config: str | dict[str, Any] | None = "") -> str:
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"allow_toys": False,
|
"allow_toys": False,
|
||||||
@@ -192,6 +209,28 @@ def _position_filter(focus: str, family: str, positions: list[str] | tuple[str,
|
|||||||
return _action_filter(focus, position_config)
|
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:
|
def _coworking_location_config() -> str:
|
||||||
return pb.build_location_pool_json(
|
return pb.build_location_pool_json(
|
||||||
enabled=True,
|
enabled=True,
|
||||||
@@ -967,6 +1006,135 @@ def smoke_pov_penetration_position_routes() -> None:
|
|||||||
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
|
_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:
|
def smoke_no_expression_fallback() -> None:
|
||||||
cast = pb.build_character_slot_json(
|
cast = pb.build_character_slot_json(
|
||||||
subject_type="woman",
|
subject_type="woman",
|
||||||
@@ -1101,6 +1269,8 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
|
|||||||
("pov_outercourse_position_routes", smoke_pov_outercourse_position_routes),
|
("pov_outercourse_position_routes", smoke_pov_outercourse_position_routes),
|
||||||
("pov_oral_position_routes", smoke_pov_oral_position_routes),
|
("pov_oral_position_routes", smoke_pov_oral_position_routes),
|
||||||
("pov_penetration_position_routes", smoke_pov_penetration_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),
|
("expression_disabled", smoke_no_expression_fallback),
|
||||||
("formatter_metadata_fixtures", smoke_formatter_metadata_fixtures),
|
("formatter_metadata_fixtures", smoke_formatter_metadata_fixtures),
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user