diff --git a/docs/prompt-architecture-improvement-plan.md b/docs/prompt-architecture-improvement-plan.md index b8d69b5..959d548 100644 --- a/docs/prompt-architecture-improvement-plan.md +++ b/docs/prompt-architecture-improvement-plan.md @@ -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. +- 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. - camera-scene prose and coworking composition adaptation live in `scene_camera_adapters.py`; `prompt_builder.py` still owns camera config parsing and row mutation. @@ -358,7 +361,7 @@ Medium-term: 1. Split `__init__.py` node classes by family after behavior is covered by smoke checks. -2. Split the internals of `hardcore_role_graphs.py` by action family once more - generated edge cases are covered by smoke fixtures. +2. Continue splitting the internals of `hardcore_role_graphs.py` by action + family once generated edge cases are covered by smoke fixtures. 3. Add more route-level smoke fixtures for generated edge cases that are not covered by the current static Krea/SDXL/caption metadata fixtures. diff --git a/docs/prompt-pool-routing-map.md b/docs/prompt-pool-routing-map.md index 34ba681..0e0777e 100644 --- a/docs/prompt-pool-routing-map.md +++ b/docs/prompt-pool-routing-map.md @@ -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_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. | | `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. | diff --git a/hardcore_role_graphs.py b/hardcore_role_graphs.py index bf7a80b..44d200e 100644 --- a/hardcore_role_graphs.py +++ b/hardcore_role_graphs.py @@ -4,6 +4,11 @@ import random import re from typing import Any +try: + from .hardcore_role_outercourse import build_outercourse_role_graph +except ImportError: # Allows local smoke tests with `python -c`. + from hardcore_role_outercourse import build_outercourse_role_graph + def _lettered(prefix: str, count: int) -> list[str]: letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -293,76 +298,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} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass." - def outercourse_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 - if any(term in text for term in ("boobjob", "titjob", "breast-sex", "breast sex")): - if man_is_pov: - return ( - f"{woman} kneels between the POV viewer's open thighs with her torso bent forward over his pelvis and shoulders low, " - "both hands lifting and pressing her breasts tightly around the POV viewer's penis shaft while the glans sits just below her lips." - ) - return ( - f"{woman} kneels between {man}'s open thighs with her torso bent forward over his pelvis and shoulders low while {man} sits with legs apart, " - f"{woman}'s hands lifting and pressing her breasts tightly around {man}'s penis shaft while the glans sits just below her lips." - ) - if any(term in text for term in ("testicle", "balls-licking", "balls licking", "balls and mouth", "balls held")): - if man_is_pov: - return ( - f"{woman} kneels very low between the POV viewer's open thighs with her torso bent forward and shoulders between his knees, " - "head tucked under the penis shaft at the base of the penis, mouth and tongue on the POV viewer's balls while his penis points upward above her face." - ) - return ( - f"{man} sits with legs apart while {woman} kneels very low between his open thighs with her torso bent forward and shoulders between his knees, " - f"head tucked under the penis shaft at the base of his penis, mouth and tongue on his balls while {man}'s penis points upward above her face." - ) - if "penis-licking" in position_text or "penis licking" in text or "tongue along" in text or "tongue licking" in text: - if man_is_pov: - return ( - f"{woman} bends forward between the POV viewer's open thighs, head low under the POV viewer's penis with her face directly under the penis, " - "tongue running along the underside from the penis shaft to the glans while one hand steadies the base of the penis." - ) - return ( - f"{woman} bends forward between {man}'s open thighs, head low under {man}'s penis with her face directly under the penis, " - f"tongue running along the underside from the penis shaft to the glans while one hand steadies the base of the penis." - ) - if "footjob" in text or "soles" in text or "toes curled" in text or "feet stroking" in text: - if man_is_pov: - return ( - f"{woman} faces the POV viewer with her hips back, torso visible behind her raised legs, and both knees bent open toward the camera, " - "both soles wrapped around the POV viewer's penis shaft in the lower foreground." - ) - return ( - f"{man} reclines with hips forward while {woman} faces him with her hips back and both knees bent open, " - f"wrapping both soles around {man}'s penis shaft while the contact stays centered." - ) - if "handjob" in position_text or "handjob" in text or "hand job" in text or "hand wrapped" in text: - if man_is_pov: - return ( - f"{woman} kneels between the POV viewer's open thighs with her torso leaning forward and face visible behind the penis shaft, " - "one hand wrapped around the POV viewer's penis shaft while the other hand steadies the base of the penis as she strokes toward the glans." - ) - return ( - f"{woman} kneels between {man}'s open thighs with her torso leaning forward and face visible behind the penis shaft, " - f"one hand wrapped around {man}'s penis shaft while the other hand steadies the base of the penis as she strokes toward the glans." - ) - if man_is_pov: - return ( - f"{woman} kneels close to the POV viewer's hips and keeps the POV viewer's penis centered in clear non-penetrative contact, " - "with her mouth, hands, breasts, or feet visibly working around the penis shaft." - ) - return ( - f"{woman} kneels close to {man}'s hips and keeps {man}'s penis centered in clear non-penetrative contact, " - "with her mouth, hands, breasts, or feet visibly working around the penis shaft." - ) - def oral_position_graph(woman: str, man: str) -> str: position_text = str((item_axis_values or {}).get("position") or "").lower() text = " ".join( @@ -543,7 +478,7 @@ def build_hardcore_role_graph( elif "foreplay" in slug: graph = foreplay_position_graph(woman, man) elif "outercourse" in slug: - graph = outercourse_position_graph(woman, man) + graph = build_outercourse_role_graph(woman, man, item_text, item_axis_values, pov_labels) elif "oral" in slug: graph = oral_position_graph(woman, man) elif "anal" in slug or "double" in slug: diff --git a/hardcore_role_outercourse.py b/hardcore_role_outercourse.py new file mode 100644 index 0000000..55b34e7 --- /dev/null +++ b/hardcore_role_outercourse.py @@ -0,0 +1,84 @@ +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_outercourse_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 []) + if any(term in text for term in ("boobjob", "titjob", "breast-sex", "breast sex")): + if man_is_pov: + return ( + f"{woman} kneels between the POV viewer's open thighs with her torso bent forward over his pelvis and shoulders low, " + "both hands lifting and pressing her breasts tightly around the POV viewer's penis shaft while the glans sits just below her lips." + ) + return ( + f"{woman} kneels between {man}'s open thighs with her torso bent forward over his pelvis and shoulders low while {man} sits with legs apart, " + f"{woman}'s hands lifting and pressing her breasts tightly around {man}'s penis shaft while the glans sits just below her lips." + ) + if any(term in text for term in ("testicle", "balls-licking", "balls licking", "balls and mouth", "balls held")): + if man_is_pov: + return ( + f"{woman} kneels very low between the POV viewer's open thighs with her torso bent forward and shoulders between his knees, " + "head tucked under the penis shaft at the base of the penis, mouth and tongue on the POV viewer's balls while his penis points upward above her face." + ) + return ( + f"{man} sits with legs apart while {woman} kneels very low between his open thighs with her torso bent forward and shoulders between his knees, " + f"head tucked under the penis shaft at the base of his penis, mouth and tongue on his balls while {man}'s penis points upward above her face." + ) + if "penis-licking" in position_text or "penis licking" in text or "tongue along" in text or "tongue licking" in text: + if man_is_pov: + return ( + f"{woman} bends forward between the POV viewer's open thighs, head low under the POV viewer's penis with her face directly under the penis, " + "tongue running along the underside from the penis shaft to the glans while one hand steadies the base of the penis." + ) + return ( + f"{woman} bends forward between {man}'s open thighs, head low under {man}'s penis with her face directly under the penis, " + f"tongue running along the underside from the penis shaft to the glans while one hand steadies the base of the penis." + ) + if "footjob" in text or "soles" in text or "toes curled" in text or "feet stroking" in text: + if man_is_pov: + return ( + f"{woman} faces the POV viewer with her hips back, torso visible behind her raised legs, and both knees bent open toward the camera, " + "both soles wrapped around the POV viewer's penis shaft in the lower foreground." + ) + return ( + f"{man} reclines with hips forward while {woman} faces him with her hips back and both knees bent open, " + f"wrapping both soles around {man}'s penis shaft while the contact stays centered." + ) + if "handjob" in position_text or "handjob" in text or "hand job" in text or "hand wrapped" in text: + if man_is_pov: + return ( + f"{woman} kneels between the POV viewer's open thighs with her torso leaning forward and face visible behind the penis shaft, " + "one hand wrapped around the POV viewer's penis shaft while the other hand steadies the base of the penis as she strokes toward the glans." + ) + return ( + f"{woman} kneels between {man}'s open thighs with her torso leaning forward and face visible behind the penis shaft, " + f"one hand wrapped around {man}'s penis shaft while the other hand steadies the base of the penis as she strokes toward the glans." + ) + if man_is_pov: + return ( + f"{woman} kneels close to the POV viewer's hips and keeps the POV viewer's penis centered in clear non-penetrative contact, " + "with her mouth, hands, breasts, or feet visibly working around the penis shaft." + ) + return ( + f"{woman} kneels close to {man}'s hips and keeps {man}'s penis centered in clear non-penetrative contact, " + "with her mouth, hands, breasts, or feet visibly working around the penis shaft." + )