From 79661c396acc685a786d8ad944f46ec8f9ad03f0 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Wed, 24 Jun 2026 21:13:19 +0200 Subject: [PATCH] Fix toy-assisted double penetration prompts --- categories/expression_composition_pools.json | 2 +- categories/sexual_poses.json | 37 ++++++-- krea_formatter.py | 92 ++++++++++++++++++-- prompt_builder.py | 16 +++- 4 files changed, 130 insertions(+), 17 deletions(-) diff --git a/categories/expression_composition_pools.json b/categories/expression_composition_pools.json index 267c9d1..9368425 100644 --- a/categories/expression_composition_pools.json +++ b/categories/expression_composition_pools.json @@ -489,7 +489,7 @@ {"text": "close crop on hips, hands, and overwhelmed face", "min_people": 2, "max_people": 3}, {"text": "wide frame with all penetration points visible", "min_people": 3}, {"text": "kneeling double-contact composition", "min_people": 3}, - {"text": "overhead tangled-body anal frame", "min_people": 2, "max_people": 3} + {"text": "overhead tangled-body anal frame", "min_people": 3} ], "threesome_compositions": [ {"text": "centered threesome composition with all three adult bodies visible", "min_people": 3, "max_people": 3}, diff --git a/categories/sexual_poses.json b/categories/sexual_poses.json index d6ec824..1ed8bc1 100644 --- a/categories/sexual_poses.json +++ b/categories/sexual_poses.json @@ -456,11 +456,36 @@ "text": "two partners penetrating at once", "min_people": 3 }, - "stacked bodies on the bed", - "kneeling center partner", - "one partner held between two bodies", - "front-and-back contact", - "three bodies locked together" + { + "text": "rear-entry body alignment", + "min_people": 2, + "max_people": 2 + }, + { + "text": "toy-assisted second contact aligned behind the body", + "min_people": 2, + "max_people": 2 + }, + { + "text": "stacked bodies in close contact", + "min_people": 3 + }, + { + "text": "kneeling center partner", + "min_people": 3 + }, + { + "text": "one partner held between two bodies", + "min_people": 3 + }, + { + "text": "front-and-back contact", + "min_people": 3 + }, + { + "text": "three bodies locked together", + "min_people": 3 + } ], "body_contact": [ "hands spreading the ass", @@ -521,7 +546,7 @@ "hands gripping hips", "hands holding the waist", "one hand spreading cheeks", - "hands braced on the bed", + "hands braced beside the body", "fingers digging into thighs", "hands holding shoulders", "one hand cupping a breast", diff --git a/krea_formatter.py b/krea_formatter.py index 5f49284..cfdffc9 100644 --- a/krea_formatter.py +++ b/krea_formatter.py @@ -42,7 +42,10 @@ def _clean(value: Any) -> str: HARDCORE_ENVIRONMENT_ANCHOR_REPLACEMENTS = ( - (r"\bstacked bodies on the bed\b", "stacked bodies with close body alignment"), + (r"\bstacked bodies on the bed\b", "close body alignment"), + (r"\bstacked bodies with close body alignment\b", "close body alignment"), + (r"\boverhead tangled-body anal frame\b", "overhead rear-entry anal frame"), + (r"\btangled-body\b", "close-body"), (r"\bthree bodies tangled on the bed\b", "three bodies tangled in close contact"), (r"\ba triangle of bodies on the mattress\b", "a triangle of bodies in close contact"), (r"\bbodies tangled on the sheets\b", "bodies tangled in close contact"), @@ -503,6 +506,23 @@ def _position_context_text(role_graph: str, hard_item: str, composition: str = " ) +def _is_toy_assisted_double_text(*parts: Any) -> bool: + text = " ".join(_clean(part).lower() for part in parts if _clean(part)) + if "toy" not in text: + return False + return any( + token in text + for token in ( + "double penetration", + "double-penetration", + "vaginal and anal penetration", + "second penetration point", + "second point of contact", + "second contact", + ) + ) + + def _mentions_ass(text: str) -> bool: return bool( re.search( @@ -526,6 +546,22 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = "" item_text = " ".join(part for part in (_clean(hard_item).lower(), _axis_values_text(axis_values).lower()) if part) if not text: return "" + if _is_toy_assisted_double_text(role_graph, hard_item, composition, _axis_values_text(axis_values)): + if "face-down ass-up" in text or "face-down" in text: + return "toy-assisted face-down rear-entry double-penetration pose" + if "doggy style" in text or "doggy-style" in text or "all fours" in text or "rear-entry" in text: + return "toy-assisted rear-entry double-penetration pose" + if "bent-over" in text or "bent forward" in text: + return "toy-assisted bent-over double-penetration pose" + if "spooning anal" in text or "side-lying anal" in text or "side-lying" in text: + return "toy-assisted side-lying double-penetration pose" + if "edge-supported" in text or "bed-edge" in text or "edge-of-bed" in text: + return "toy-assisted edge-supported double-penetration pose" + if "standing anal" in text or "standing supported" in text or "standing" in text: + return "toy-assisted standing double-penetration pose" + if "kneeling anal" in text or "kneeling" in text: + return "toy-assisted kneeling rear-entry double-penetration pose" + return "toy-assisted rear-entry double-penetration pose" if "double penetration" in text or "vaginal and anal penetration" in text or "front-and-back" in text: if "face-down ass-up" in text: return "face-down rear-entry double-penetration pose" @@ -633,7 +669,7 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com return mixed if mixed_woman_man else generic def double_tail() -> str: - return ", with the toy aligned at the second penetration point" if "toy" in text else ", with the second penetration point aligned" + return "" if "toy" in text else ", with the second penetration point aligned" if "sixty-nine" in text: return cast_phrase( @@ -767,8 +803,8 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com if "double-penetration" in text or "double penetration" in text: if "toy" in text: return cast_phrase( - "with the woman on all fours, the man behind her, and the toy aligned at the second penetration point", - "with the receiving body on all fours and the toy aligned at the second penetration point", + "with the woman on all fours and the man positioned behind her at hip level", + "with the receiving body on all fours and the penetrating partner positioned behind at hip level", ) if "from the front" in text: return cast_phrase( @@ -953,6 +989,44 @@ def _dedupe_hardcore_detail(detail: str, anchor: str) -> str: return _clean(detail).strip(" ,;") +def _dedupe_toy_double_detail(detail: str) -> str: + detail = _clean(detail) + if not detail: + return "" + angle_view = ( + r"(?:rear-view|side-profile|low-angle|mirror-reflected|overhead|close-up|wide full-body|front-facing with hips turned)" + ) + toy_act = ( + r"(?:penis and toy double penetration|toy-assisted vaginal and anal penetration at the same time|toy and strap-on double penetration)" + ) + detail = re.sub( + rf"\b({angle_view}\s+view of\s+){toy_act}\b", + r"\1the rear-entry contact", + detail, + flags=re.IGNORECASE, + ) + detail = re.sub(rf",?\s*\b{toy_act}\b", "", detail, flags=re.IGNORECASE) + duplicate_phrases = ( + "toy-assisted second contact aligned behind the body", + "toy aligned for a second penetration point", + "rear-entry body alignment", + "close body alignment", + "stacked bodies in close contact", + "one body between two partners", + "one partner behind and one partner in front", + "two partners penetrating at once", + "one partner held between two bodies", + "front-and-back contact", + "three bodies locked together", + "kneeling center partner", + ) + for phrase in duplicate_phrases: + detail = re.sub(rf",?\s*\b{re.escape(phrase)}\b", "", detail, flags=re.IGNORECASE) + detail = re.sub(r"^\s*,\s*", "", detail) + detail = re.sub(r",\s*,", ",", detail) + return _clean(detail).strip(" ,;") + + def _detail_clauses(detail: str) -> list[str]: return [part.strip(" ,;") for part in re.split(r",\s*(?:and\s+)?", _clean(detail)) if part.strip(" ,;")] @@ -1205,13 +1279,13 @@ def _hardcore_action_sentence( hard_item = _clean(hard_item).rstrip(".") role_graph = re.sub( r"\bthe man penetrates the woman while a toy adds a second point of contact\b", - "the man's penis thrusts into the woman while a toy adds a second penetration point", + "the man's penis thrusts into the woman while a toy is positioned at the second penetration point", role_graph, flags=re.IGNORECASE, ) role_graph = re.sub( r"\bthe man thrusts his penis into the woman while a toy adds a second penetration point\b", - "the man's penis thrusts into the woman while a toy adds the second penetration point", + "the man's penis thrusts into the woman while a toy is positioned at the second penetration point", role_graph, flags=re.IGNORECASE, ) @@ -1256,10 +1330,10 @@ def _hardcore_action_sentence( role_graph = _climax_role_graph(role_graph, hard_item, axis_values) detail = _hardcore_item_detail(hard_item) anchor = _hardcore_pose_anchor(role_graph, hard_item, composition, axis_values) - if "double-penetration" in anchor.lower() and "toy" in role_graph.lower(): + if _is_toy_assisted_double_text(role_graph, hard_item, composition, _axis_values_text(axis_values)): role_graph = re.sub( r"\s+while a toy adds (?:the|a) second penetration point\b", - "", + " while a toy is positioned at the second penetration point", role_graph, flags=re.IGNORECASE, ) @@ -1268,6 +1342,8 @@ def _hardcore_action_sentence( detail = _dedupe_climax_detail(detail, role_graph, detail_density) else: detail = _dedupe_hardcore_detail(detail, anchor) if anchor else detail + if _is_toy_assisted_double_text(role_graph, hard_item, composition, _axis_values_text(axis_values)): + detail = _dedupe_toy_double_detail(detail) detail = _limit_detail_for_density(detail, detail_density, False) arrangement = _hardcore_pose_arrangement(anchor, role_graph, hard_item, composition, axis_values) anchor_phrase = _with_indefinite_article(anchor) if anchor else "" diff --git a/prompt_builder.py b/prompt_builder.py index bf7d394..685bd30 100644 --- a/prompt_builder.py +++ b/prompt_builder.py @@ -643,7 +643,10 @@ def _heuristic_cast_compatible(text: str, women_count: int, men_count: int) -> b HARDCORE_ENVIRONMENT_ANCHOR_REPLACEMENTS = ( - (r"\bstacked bodies on the bed\b", "stacked bodies with close body alignment"), + (r"\bstacked bodies on the bed\b", "close body alignment"), + (r"\bstacked bodies with close body alignment\b", "close body alignment"), + (r"\boverhead tangled-body anal frame\b", "overhead rear-entry anal frame"), + (r"\btangled-body\b", "close-body"), (r"\bthree bodies tangled on the bed\b", "three bodies tangled in close contact"), (r"\ba triangle of bodies on the mattress\b", "a triangle of bodies in close contact"), (r"\bbodies tangled on the sheets\b", "bodies tangled in close contact"), @@ -3576,7 +3579,16 @@ def _role_graph( if people_count >= 3: graph = f"{man} thrusts his penis into {woman} while {third} adds a second penetration point from the front." else: - graph = f"{man} thrusts his penis into {woman} while a toy adds a second penetration point." + 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; {man}'s penis thrusts into {woman} while a toy is positioned at the second penetration point." + elif "face-down" in item_text: + graph = f"{woman} lies face-down with hips raised while {man} is positioned behind her; {man}'s penis thrusts into {woman} while a toy is positioned at the second penetration point." + elif "standing" in item_text: + graph = f"{woman} stands braced with hips raised while {man} is positioned behind her; {man}'s penis thrusts into {woman} while a toy is positioned at the second penetration point." + elif "kneeling" in item_text: + graph = f"{woman} kneels forward with hips raised while {man} is positioned behind her; {man}'s penis thrusts into {woman} while a toy is positioned at the second penetration point." + else: + graph = f"{woman} is on all fours with hips raised while {man} is positioned behind her; {man}'s penis thrusts into {woman} while a toy is positioned at the second penetration point." elif people_count >= 3: graph = f"{man} thrusts his penis into {woman} while {third} gives oral contact from the front." else: