Extract oral role graph wording
This commit is contained in:
@@ -105,6 +105,9 @@ Already isolated:
|
|||||||
- outercourse-specific role graph wording has started moving into action-family
|
- outercourse-specific role graph wording has started moving into action-family
|
||||||
modules; `hardcore_role_outercourse.py` owns boobjob, testicle-sucking,
|
modules; `hardcore_role_outercourse.py` owns boobjob, testicle-sucking,
|
||||||
penis-licking, handjob, and footjob body geometry.
|
penis-licking, handjob, and footjob body geometry.
|
||||||
|
- 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.
|
||||||
- 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.
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ Core helper ownership:
|
|||||||
| Python module | What it owns |
|
| 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_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_outercourse.py` | Outercourse role graph wording for boobjob, testicle-sucking, penis-licking, handjob, and footjob 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. |
|
||||||
@@ -733,6 +734,9 @@ pair metadata through the core Python APIs, then verifies:
|
|||||||
suffix;
|
suffix;
|
||||||
- POV outercourse routes keep constrained boobjob, testicle-sucking,
|
- POV outercourse routes keep constrained boobjob, testicle-sucking,
|
||||||
penis-licking, handjob, and footjob geometry through Krea formatting;
|
penis-licking, handjob, and footjob geometry through Krea formatting;
|
||||||
|
- 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;
|
||||||
- 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
|
||||||
|
|||||||
+3
-96
@@ -5,8 +5,10 @@ import re
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
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
|
||||||
except ImportError: # Allows local smoke tests with `python -c`.
|
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_outercourse import build_outercourse_role_graph
|
||||||
|
|
||||||
|
|
||||||
@@ -53,7 +55,6 @@ def build_hardcore_role_graph(
|
|||||||
people = participants["people"]
|
people = participants["people"]
|
||||||
slug = str(subcategory.get("slug") or subcategory.get("name") or "").lower()
|
slug = str(subcategory.get("slug") or subcategory.get("name") or "").lower()
|
||||||
item_text = " ".join((item_axis_values or {}).values()).lower()
|
item_text = " ".join((item_axis_values or {}).values()).lower()
|
||||||
pov_set = set(pov_labels or [])
|
|
||||||
|
|
||||||
def any_person(exclude: set[str] | None = None) -> str:
|
def any_person(exclude: set[str] | None = None) -> str:
|
||||||
exclude = exclude or set()
|
exclude = exclude or set()
|
||||||
@@ -298,100 +299,6 @@ def build_hardcore_role_graph(
|
|||||||
return f"{woman} kneels forward with hips raised while {man} kneels behind her and thrusts his penis into her ass."
|
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."
|
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||||
|
|
||||||
def oral_position_graph(woman: str, man: str) -> str:
|
|
||||||
position_text = str((item_axis_values or {}).get("position") or "").lower()
|
|
||||||
text = " ".join(
|
|
||||||
str(part or "").lower()
|
|
||||||
for part in (
|
|
||||||
item_text,
|
|
||||||
*((item_axis_values or {}).values()),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
man_is_pov = man in pov_set
|
|
||||||
woman_gives = any(
|
|
||||||
term in text
|
|
||||||
for term in (
|
|
||||||
"fellatio",
|
|
||||||
"blowjob",
|
|
||||||
"deepthroat",
|
|
||||||
"penis sucking",
|
|
||||||
"penis in mouth",
|
|
||||||
"penis in her mouth",
|
|
||||||
"mouth stretched around a penis",
|
|
||||||
"lips wrapped",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
man_gives = any(
|
|
||||||
term in text
|
|
||||||
for term in (
|
|
||||||
"cunnilingus",
|
|
||||||
"pussy licking",
|
|
||||||
"tongue on pussy",
|
|
||||||
"mouth on pussy",
|
|
||||||
"pussy and tongue",
|
|
||||||
"face-sitting",
|
|
||||||
"tongue contact clearly visible",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if "mouth on genitals" in text and not woman_gives and not man_gives:
|
|
||||||
if any(term in text for term in ("face-sitting", "reclining", "straddled", "spread-leg", "open thighs")):
|
|
||||||
man_gives = True
|
|
||||||
else:
|
|
||||||
woman_gives = True
|
|
||||||
|
|
||||||
if "sixty-nine" in position_text or ("sixty-nine" in text and not position_text):
|
|
||||||
return f"{woman} and {man} lie head-to-hips in a sixty-nine position, with {woman}'s mouth on {man}'s penis and {man}'s mouth on {woman}'s pussy."
|
|
||||||
if "face-sitting" in position_text or ("face-sitting" in text and not position_text):
|
|
||||||
if man_is_pov:
|
|
||||||
return (
|
|
||||||
f"{woman} is above the POV camera, straddling the POV viewer's face with thighs on both sides of his head, "
|
|
||||||
"pussy directly over the POV viewer's mouth for close first-person underview tongue contact."
|
|
||||||
)
|
|
||||||
return f"{man} lies on his back while {woman} straddles his face with her thighs around his head and {man}'s mouth pressed to her pussy."
|
|
||||||
if "straddled oral" in position_text or ("straddled oral" in text and not position_text):
|
|
||||||
if woman_gives and not man_gives:
|
|
||||||
return f"{man} straddles forward near {woman}'s face while {woman} kneels below him with her mouth on his penis."
|
|
||||||
return f"{woman} straddles above {man}'s face with her thighs framing his head while {man}'s mouth stays pressed to her pussy."
|
|
||||||
if "side-lying oral" in position_text or ("side-lying oral" in text and not position_text):
|
|
||||||
if woman_gives and not man_gives:
|
|
||||||
return f"{man} lies on his side with hips angled toward {woman} while {woman} lies beside his thighs and takes his penis in her mouth."
|
|
||||||
return f"{woman} lies on her side with her top thigh lifted while {man} lies beside her hips with his mouth pressed to her pussy."
|
|
||||||
if (
|
|
||||||
"edge-of-bed oral" in position_text
|
|
||||||
or "edge of bed oral" in position_text
|
|
||||||
or "edge-supported oral" in position_text
|
|
||||||
or (("edge-of-bed oral" in text or "edge of bed oral" in text or "edge-supported oral" in text) and not position_text)
|
|
||||||
):
|
|
||||||
if woman_gives and not man_gives:
|
|
||||||
return f"{man} sits at a raised edge with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
|
||||||
return f"{woman} lies at a raised edge with thighs open while {man} kneels between her legs with his mouth on her pussy."
|
|
||||||
if "standing oral" in position_text or ("standing oral" in text and not position_text):
|
|
||||||
if man_gives and not woman_gives:
|
|
||||||
return f"{woman} stands braced with one thigh lifted while {man} kneels between her legs with his mouth on her pussy."
|
|
||||||
return f"{man} stands with hips forward while {woman} kneels in front of him at hip height and takes his penis in her mouth."
|
|
||||||
if "chair oral" in position_text or ("chair oral" in text and not position_text):
|
|
||||||
if man_gives and not woman_gives:
|
|
||||||
return f"{woman} sits in a chair with thighs open while {man} kneels between her legs with his mouth pressed to her pussy."
|
|
||||||
return f"{man} sits in a chair with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
|
||||||
if (
|
|
||||||
"reclining cunnilingus" in position_text
|
|
||||||
or "spread-leg oral" in position_text
|
|
||||||
or (("reclining cunnilingus" in text or "spread-leg oral" in text) and not position_text)
|
|
||||||
):
|
|
||||||
if woman_gives and not man_gives:
|
|
||||||
return f"{man} reclines with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
|
||||||
return f"{woman} reclines on her back with thighs spread while {man} kneels between her legs with his mouth on her pussy."
|
|
||||||
if "kneeling oral" in position_text or ("kneeling oral" in text and not position_text):
|
|
||||||
if man_gives and not woman_gives:
|
|
||||||
return f"{woman} kneels with thighs parted and hips angled forward while {man} kneels in front of her with his mouth on her pussy."
|
|
||||||
return (
|
|
||||||
f"{woman} kneels in front of {man}'s penis while {man} stands over her; "
|
|
||||||
f"{woman} takes {man}'s penis in her mouth with saliva dripping on the penis as {man} looks down toward her."
|
|
||||||
)
|
|
||||||
if man_gives and not woman_gives:
|
|
||||||
return f"{woman} lies on her back with thighs open while {man} kneels between her legs with his mouth pressed to her pussy."
|
|
||||||
return f"{woman} kneels in front of {man}'s hips and takes his penis in her mouth while {man} keeps his hips aligned with her face."
|
|
||||||
|
|
||||||
if people_count == 1:
|
if people_count == 1:
|
||||||
solo = people[0]
|
solo = people[0]
|
||||||
if women_count == 1:
|
if women_count == 1:
|
||||||
@@ -480,7 +387,7 @@ def build_hardcore_role_graph(
|
|||||||
elif "outercourse" in slug:
|
elif "outercourse" in slug:
|
||||||
graph = build_outercourse_role_graph(woman, man, item_text, item_axis_values, pov_labels)
|
graph = build_outercourse_role_graph(woman, man, item_text, item_axis_values, pov_labels)
|
||||||
elif "oral" in slug:
|
elif "oral" in slug:
|
||||||
graph = oral_position_graph(woman, man)
|
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:
|
if "double" in item_text or "toy" in item_text:
|
||||||
if people_count >= 3:
|
if people_count >= 3:
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
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 _oral_direction(text: str) -> tuple[bool, bool]:
|
||||||
|
woman_gives = any(
|
||||||
|
term in text
|
||||||
|
for term in (
|
||||||
|
"fellatio",
|
||||||
|
"blowjob",
|
||||||
|
"deepthroat",
|
||||||
|
"penis sucking",
|
||||||
|
"penis in mouth",
|
||||||
|
"penis in her mouth",
|
||||||
|
"mouth stretched around a penis",
|
||||||
|
"lips wrapped",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
man_gives = any(
|
||||||
|
term in text
|
||||||
|
for term in (
|
||||||
|
"cunnilingus",
|
||||||
|
"pussy licking",
|
||||||
|
"tongue on pussy",
|
||||||
|
"mouth on pussy",
|
||||||
|
"pussy and tongue",
|
||||||
|
"face-sitting",
|
||||||
|
"tongue contact clearly visible",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if "mouth on genitals" in text and not woman_gives and not man_gives:
|
||||||
|
if any(term in text for term in ("face-sitting", "reclining", "straddled", "spread-leg", "open thighs")):
|
||||||
|
man_gives = True
|
||||||
|
else:
|
||||||
|
woman_gives = True
|
||||||
|
return woman_gives, man_gives
|
||||||
|
|
||||||
|
|
||||||
|
def build_oral_role_graph(
|
||||||
|
woman: str,
|
||||||
|
man: str,
|
||||||
|
item_text: str,
|
||||||
|
item_axis_values: dict[str, Any] | None = None,
|
||||||
|
pov_labels: list[str] | None = None,
|
||||||
|
) -> str:
|
||||||
|
position_text = str((item_axis_values or {}).get("position") or "").lower()
|
||||||
|
text = _context_text(item_text, item_axis_values)
|
||||||
|
man_is_pov = man in set(pov_labels or [])
|
||||||
|
woman_gives, man_gives = _oral_direction(text)
|
||||||
|
|
||||||
|
if "sixty-nine" in position_text or ("sixty-nine" in text and not position_text):
|
||||||
|
if man_is_pov:
|
||||||
|
return (
|
||||||
|
f"{woman} and the viewer lie head-to-hips in a sixty-nine position, "
|
||||||
|
f"with {woman}'s mouth on the viewer's penis and the viewer's mouth on {woman}'s pussy."
|
||||||
|
)
|
||||||
|
return f"{woman} and {man} lie head-to-hips in a sixty-nine position, with {woman}'s mouth on {man}'s penis and {man}'s mouth on {woman}'s pussy."
|
||||||
|
if "face-sitting" in position_text or ("face-sitting" in text and not position_text):
|
||||||
|
if man_is_pov:
|
||||||
|
return (
|
||||||
|
f"{woman} is above the POV camera, straddling the viewer's face with thighs on both sides of his head, "
|
||||||
|
"pussy directly over the viewer's mouth for close first-person underview tongue contact."
|
||||||
|
)
|
||||||
|
return f"{man} lies on his back while {woman} straddles his face with her thighs around his head and {man}'s mouth pressed to her pussy."
|
||||||
|
if "straddled oral" in position_text or ("straddled oral" in text and not position_text):
|
||||||
|
if woman_gives and not man_gives:
|
||||||
|
if man_is_pov:
|
||||||
|
return f"The viewer straddles forward near {woman}'s face while {woman} kneels below him with her mouth on his penis."
|
||||||
|
return f"{man} straddles forward near {woman}'s face while {woman} kneels below him with her mouth on his penis."
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} straddles above the viewer's face with her thighs framing his head while the viewer's mouth stays pressed to her pussy."
|
||||||
|
return f"{woman} straddles above {man}'s face with her thighs framing his head while {man}'s mouth stays pressed to her pussy."
|
||||||
|
if "side-lying oral" in position_text or ("side-lying oral" in text and not position_text):
|
||||||
|
if woman_gives and not man_gives:
|
||||||
|
if man_is_pov:
|
||||||
|
return f"The viewer lies on his side with hips angled toward {woman} while {woman} lies beside his thighs and takes the viewer's penis in her mouth."
|
||||||
|
return f"{man} lies on his side with hips angled toward {woman} while {woman} lies beside his thighs and takes his penis in her mouth."
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} lies on her side with her top thigh lifted while the viewer lies beside her hips with his mouth pressed to her pussy."
|
||||||
|
return f"{woman} lies on her side with her top thigh lifted while {man} lies beside her hips with his mouth pressed to her pussy."
|
||||||
|
if (
|
||||||
|
"edge-of-bed oral" in position_text
|
||||||
|
or "edge of bed oral" in position_text
|
||||||
|
or "edge-supported oral" in position_text
|
||||||
|
or (("edge-of-bed oral" in text or "edge of bed oral" in text or "edge-supported oral" in text) and not position_text)
|
||||||
|
):
|
||||||
|
if woman_gives and not man_gives:
|
||||||
|
if man_is_pov:
|
||||||
|
return f"The viewer sits at a raised edge with legs apart while {woman} kneels between his thighs and takes the viewer's penis in her mouth."
|
||||||
|
return f"{man} sits at a raised edge with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} lies at a raised edge with thighs open while the viewer kneels between her legs with his mouth on her pussy."
|
||||||
|
return f"{woman} lies at a raised edge with thighs open while {man} kneels between her legs with his mouth on her pussy."
|
||||||
|
if "standing oral" in position_text or ("standing oral" in text and not position_text):
|
||||||
|
if man_gives and not woman_gives:
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} stands braced with one thigh lifted while the viewer kneels between her legs with his mouth on her pussy."
|
||||||
|
return f"{woman} stands braced with one thigh lifted while {man} kneels between her legs with his mouth on her pussy."
|
||||||
|
if man_is_pov:
|
||||||
|
return f"The viewer stands with hips forward while {woman} kneels in front of him at hip height and takes the viewer's penis in her mouth."
|
||||||
|
return f"{man} stands with hips forward while {woman} kneels in front of him at hip height and takes his penis in her mouth."
|
||||||
|
if "chair oral" in position_text or ("chair oral" in text and not position_text):
|
||||||
|
if man_gives and not woman_gives:
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} sits in a chair with thighs open while the viewer kneels between her legs with his mouth pressed to her pussy."
|
||||||
|
return f"{woman} sits in a chair with thighs open while {man} kneels between her legs with his mouth pressed to her pussy."
|
||||||
|
if man_is_pov:
|
||||||
|
return f"The viewer sits in a chair with legs apart while {woman} kneels between his thighs and takes the viewer's penis in her mouth."
|
||||||
|
return f"{man} sits in a chair with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
||||||
|
if (
|
||||||
|
"reclining cunnilingus" in position_text
|
||||||
|
or "spread-leg oral" in position_text
|
||||||
|
or (("reclining cunnilingus" in text or "spread-leg oral" in text) and not position_text)
|
||||||
|
):
|
||||||
|
if woman_gives and not man_gives:
|
||||||
|
if man_is_pov:
|
||||||
|
return f"The viewer reclines with legs apart while {woman} kneels between his thighs and takes the viewer's penis in her mouth."
|
||||||
|
return f"{man} reclines with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} reclines on her back with thighs spread while the viewer kneels between her legs with his mouth on her pussy."
|
||||||
|
return f"{woman} reclines on her back with thighs spread while {man} kneels between her legs with his mouth on her pussy."
|
||||||
|
if "kneeling oral" in position_text or ("kneeling oral" in text and not position_text):
|
||||||
|
if man_gives and not woman_gives:
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} kneels with thighs parted and hips angled forward while the viewer kneels in front of her with his mouth on her pussy."
|
||||||
|
return f"{woman} kneels with thighs parted and hips angled forward while {man} kneels in front of her with his mouth on her pussy."
|
||||||
|
if man_is_pov:
|
||||||
|
return (
|
||||||
|
f"{woman} kneels in front of the viewer's penis while he stands over her; "
|
||||||
|
f"{woman} takes the viewer's penis in her mouth with saliva dripping on the penis as he looks down toward her."
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
f"{woman} kneels in front of {man}'s penis while {man} stands over her; "
|
||||||
|
f"{woman} takes {man}'s penis in her mouth with saliva dripping on the penis as {man} looks down toward her."
|
||||||
|
)
|
||||||
|
if man_gives and not woman_gives:
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} lies on her back with thighs open while the viewer kneels between her legs with his mouth pressed to her pussy."
|
||||||
|
return f"{woman} lies on her back with thighs open while {man} kneels between her legs with his mouth pressed to her pussy."
|
||||||
|
if man_is_pov:
|
||||||
|
return f"{woman} kneels in front of the viewer's hips and takes the viewer's penis in her mouth while he keeps his hips aligned with her face."
|
||||||
|
return f"{woman} kneels in front of {man}'s hips and takes his penis in her mouth while {man} keeps his hips aligned with her face."
|
||||||
@@ -802,6 +802,89 @@ def smoke_pov_outercourse_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_oral_position_routes() -> None:
|
||||||
|
cases = [
|
||||||
|
(
|
||||||
|
"pov_oral_kneeling",
|
||||||
|
"kneeling",
|
||||||
|
("viewer's penis", "takes the viewer's penis in her mouth"),
|
||||||
|
("takes the viewer's penis in her mouth", "viewer stands over her"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"pov_oral_face_sitting",
|
||||||
|
"face_sitting",
|
||||||
|
("straddling the viewer's face", "pussy directly over the viewer's mouth"),
|
||||||
|
("straddling the viewer's face", "tongue contact visible"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"pov_oral_sixty_nine",
|
||||||
|
"sixty_nine",
|
||||||
|
("head-to-hips", "viewer's mouth on woman a's pussy"),
|
||||||
|
("head-to-hips", "viewer's mouth on the woman's pussy"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"pov_oral_edge_supported",
|
||||||
|
"edge_supported",
|
||||||
|
("raised edge with thighs open", "viewer kneels between her legs"),
|
||||||
|
("raised edge with thighs open", "viewer kneels between her legs"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"pov_oral_side_lying",
|
||||||
|
"side_lying",
|
||||||
|
("woman a lies on her side", "viewer lies beside her hips"),
|
||||||
|
("woman lies on her side", "viewer lies beside her hips"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"pov_oral_chair",
|
||||||
|
"chair_oral",
|
||||||
|
("viewer sits in a chair", "kneels between his thighs"),
|
||||||
|
("viewer sits in a chair", "kneels between the viewer's thighs"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3701):
|
||||||
|
pair = pb.build_insta_of_pair(
|
||||||
|
row_number=1,
|
||||||
|
start_index=1,
|
||||||
|
seed=offset,
|
||||||
|
ethnicity="any",
|
||||||
|
figure="random",
|
||||||
|
no_plus_women=False,
|
||||||
|
no_black=False,
|
||||||
|
trigger=Trigger,
|
||||||
|
prepend_trigger_to_prompt=True,
|
||||||
|
options_json=_insta_options(
|
||||||
|
softcore_camera_mode="from_camera_config",
|
||||||
|
hardcore_camera_mode="from_camera_config",
|
||||||
|
camera_detail="compact",
|
||||||
|
),
|
||||||
|
character_cast=_character_cast(pov_man=True),
|
||||||
|
hardcore_position_config=_position_filter("oral_only", "oral", [position_key]),
|
||||||
|
location_config=_coworking_location_config(),
|
||||||
|
hardcore_camera_config=_orbit_camera(
|
||||||
|
horizontal_angle=45,
|
||||||
|
vertical_angle=0,
|
||||||
|
zoom=7.5,
|
||||||
|
subject_focus="action",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_expect_pair(pair, name)
|
||||||
|
hard_row = pair.get("hardcore_row") or {}
|
||||||
|
_expect(hard_row.get("action_family") == "oral", f"{name} action_family should be oral")
|
||||||
|
_expect(hard_row.get("position_family") == "oral", f"{name} position_family should be oral")
|
||||||
|
_expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
|
||||||
|
role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
|
||||||
|
for term in role_terms:
|
||||||
|
_expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
|
||||||
|
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
|
||||||
|
prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
|
||||||
|
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
|
||||||
|
_expect("viewer" in prompt and "first-person" in prompt, f"{name} Krea prompt lost POV wording")
|
||||||
|
_expect("viewer lies on the viewer" not in prompt, f"{name} Krea prompt kept recursive POV wording: {prompt}")
|
||||||
|
_expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
|
||||||
|
for term in krea_terms:
|
||||||
|
_expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
|
||||||
|
|
||||||
|
|
||||||
def smoke_no_expression_fallback() -> None:
|
def smoke_no_expression_fallback() -> None:
|
||||||
cast = pb.build_character_slot_json(
|
cast = pb.build_character_slot_json(
|
||||||
subject_type="woman",
|
subject_type="woman",
|
||||||
@@ -934,6 +1017,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
|
|||||||
("pov_camera_scene", smoke_pov_camera_scene),
|
("pov_camera_scene", smoke_pov_camera_scene),
|
||||||
("krea_pov_penetration_route", smoke_krea_pov_penetration_route),
|
("krea_pov_penetration_route", smoke_krea_pov_penetration_route),
|
||||||
("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),
|
||||||
("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