Extract penetration role graph wording
This commit is contained in:
@@ -108,6 +108,9 @@ Already isolated:
|
||||
- oral-specific role graph wording lives in `hardcore_role_oral.py`, including
|
||||
direct POV viewer phrasing for kneeling, face-sitting, sixty-nine,
|
||||
edge-supported, side-lying, chair, standing, and reclining oral positions.
|
||||
- penetration-specific role graph wording lives in
|
||||
`hardcore_role_penetration.py`, covering the main vaginal penetration
|
||||
position families while Krea POV rewriting keeps first-person geometry stable.
|
||||
- camera-scene prose and coworking composition adaptation live in
|
||||
`scene_camera_adapters.py`; `prompt_builder.py` still owns camera config
|
||||
parsing and row mutation.
|
||||
|
||||
@@ -67,6 +67,7 @@ Core helper ownership:
|
||||
| `hardcore_role_graphs.py` | Source role graph construction for hardcore configured-cast rows, including POV-aware interaction 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_penetration.py` | Penetrative-sex role graph wording for missionary, cowgirl, reverse-cowgirl, doggy, standing, side-lying, raised-edge, kneeling-straddle, and lotus geometry. |
|
||||
| `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. |
|
||||
| `prompt_hygiene.py` | Generic prompt, caption, and negative-prompt cleanup. |
|
||||
@@ -737,6 +738,8 @@ pair metadata through the core Python APIs, then verifies:
|
||||
- POV oral routes keep constrained kneeling, face-sitting, sixty-nine,
|
||||
edge-supported, side-lying, and chair oral geometry through Krea formatting
|
||||
without recursive viewer wording;
|
||||
- POV penetration routes keep constrained missionary, cowgirl, reverse-cowgirl,
|
||||
doggy, raised-edge, and lotus geometry through Krea formatting;
|
||||
- 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
|
||||
|
||||
+3
-38
@@ -7,9 +7,11 @@ from typing import Any
|
||||
try:
|
||||
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_oral import build_oral_role_graph
|
||||
from hardcore_role_outercourse import build_outercourse_role_graph
|
||||
from hardcore_role_penetration import build_penetration_role_graph
|
||||
|
||||
|
||||
def _lettered(prefix: str, count: int) -> list[str]:
|
||||
@@ -238,43 +240,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} lies on her back with thighs open while {man} kneels between her legs and ejaculates semen onto her body."
|
||||
|
||||
def penetration_position_graph(woman: str, man: str) -> str:
|
||||
text = " ".join(
|
||||
str(part or "").lower()
|
||||
for part in (
|
||||
item_text,
|
||||
*((item_axis_values or {}).values()),
|
||||
)
|
||||
)
|
||||
if "missionary" in text:
|
||||
return (
|
||||
f"{woman} lies on her back with legs open around {man}'s hips while {man} is above her between her thighs; "
|
||||
f"{man}'s hips press close and {man}'s penis thrusts into her pussy."
|
||||
)
|
||||
if "reverse cowgirl" in text:
|
||||
return f"{woman} straddles {man}'s hips facing away while {man} lies under her and {man}'s penis thrusts into her pussy."
|
||||
if "cowgirl" in text or "straddling" in text:
|
||||
return f"{woman} straddles {man}'s hips facing him while {man} lies under her and {man}'s penis thrusts into her pussy."
|
||||
if "doggy" in text or "rear-entry" in text or "bent-over" in text or "bent over" in text:
|
||||
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and {man}'s penis thrusts into her pussy."
|
||||
if "standing" in text:
|
||||
return f"{woman} stands braced with hips angled back while {man} stands behind her and {man}'s penis thrusts into her pussy."
|
||||
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 {man}'s penis thrusts into her pussy."
|
||||
if "edge-of-bed" in text or "edge of bed" in text or "bed edge" in text or "edge-supported" in text or "raised edge" in text:
|
||||
return (
|
||||
f"{woman} lies back at a raised edge with hips at the edge and legs open while {man} kneels between her thighs; "
|
||||
f"{man}'s hips press close and {man}'s penis thrusts into her pussy."
|
||||
)
|
||||
if "kneeling straddle" in text:
|
||||
return f"{woman} kneels straddling {man}'s hips while {man} supports her waist and {man}'s penis thrusts into her pussy."
|
||||
if "lotus" in text:
|
||||
return f"{woman} sits in {man}'s lap facing him with legs around his hips while {man}'s penis thrusts into her pussy."
|
||||
return (
|
||||
f"{woman} lies on her back with legs spread wide and knees bent outward while {man} kneels between her open thighs facing her; "
|
||||
f"{man}'s hips are pressed between her legs and {man}'s penis thrusts into her pussy."
|
||||
)
|
||||
|
||||
def anal_position_graph(woman: str, man: str) -> str:
|
||||
text = " ".join(
|
||||
str(part or "").lower()
|
||||
@@ -414,5 +379,5 @@ def build_hardcore_role_graph(
|
||||
elif "cumshot" in slug or "climax" in slug:
|
||||
graph = climax_position_graph(woman, man, third)
|
||||
else:
|
||||
graph = penetration_position_graph(woman, man)
|
||||
graph = build_penetration_role_graph(woman, man, item_text, item_axis_values)
|
||||
return graph + support_sentence({woman, man, third} if third else {woman, man})
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
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_penetration_role_graph(
|
||||
woman: str,
|
||||
man: str,
|
||||
item_text: str,
|
||||
item_axis_values: dict[str, Any] | None = None,
|
||||
) -> str:
|
||||
text = _context_text(item_text, item_axis_values)
|
||||
if "missionary" in text:
|
||||
return (
|
||||
f"{woman} lies on her back with legs open around {man}'s hips while {man} is above her between her thighs; "
|
||||
f"{man}'s hips press close and {man}'s penis thrusts into her pussy."
|
||||
)
|
||||
if "reverse cowgirl" in text:
|
||||
return f"{woman} straddles {man}'s hips facing away while {man} lies under her and {man}'s penis thrusts into her pussy."
|
||||
if "cowgirl" in text or "straddling" in text:
|
||||
return f"{woman} straddles {man}'s hips facing him while {man} lies under her and {man}'s penis thrusts into her pussy."
|
||||
if "doggy" in text or "rear-entry" in text or "bent-over" in text or "bent over" in text:
|
||||
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and {man}'s penis thrusts into her pussy."
|
||||
if "standing" in text:
|
||||
return f"{woman} stands braced with hips angled back while {man} stands behind her and {man}'s penis thrusts into her pussy."
|
||||
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 {man}'s penis thrusts into her pussy."
|
||||
if "edge-of-bed" in text or "edge of bed" in text or "bed edge" in text or "edge-supported" in text or "raised edge" in text:
|
||||
return (
|
||||
f"{woman} lies back at a raised edge with hips at the edge and legs open while {man} kneels between her thighs; "
|
||||
f"{man}'s hips press close and {man}'s penis thrusts into her pussy."
|
||||
)
|
||||
if "kneeling straddle" in text:
|
||||
return f"{woman} kneels straddling {man}'s hips while {man} supports her waist and {man}'s penis thrusts into her pussy."
|
||||
if "lotus" in text:
|
||||
return f"{woman} sits in {man}'s lap facing him with legs around his hips while {man}'s penis thrusts into her pussy."
|
||||
return (
|
||||
f"{woman} lies on her back with legs spread wide and knees bent outward while {man} kneels between her open thighs facing her; "
|
||||
f"{man}'s hips are pressed between her legs and {man}'s penis thrusts into her pussy."
|
||||
)
|
||||
@@ -885,6 +885,88 @@ def smoke_pov_oral_position_routes() -> None:
|
||||
_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_no_expression_fallback() -> None:
|
||||
cast = pb.build_character_slot_json(
|
||||
subject_type="woman",
|
||||
@@ -1018,6 +1100,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
|
||||
("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),
|
||||
("expression_disabled", smoke_no_expression_fallback),
|
||||
("formatter_metadata_fixtures", smoke_formatter_metadata_fixtures),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user