Improve same-cast softcore and solo hardcore prompts

This commit is contained in:
2026-06-24 11:13:59 +02:00
parent 51d351679f
commit af8fe355f7
5 changed files with 162 additions and 13 deletions
+4 -1
View File
@@ -110,7 +110,10 @@ When the hardcore cast includes partners, pair mode also creates deterministic
shared cast descriptors such as `woman A / primary creator` and `man A`. Use shared cast descriptors such as `woman A / primary creator` and `man A`. Use
`softcore_cast=same_as_hardcore`, `hardcore_cast=couple`, and `softcore_cast=same_as_hardcore`, `hardcore_cast=couple`, and
`continuity=same_creator_same_room` when you want a soft prompt with the same `continuity=same_creator_same_room` when you want a soft prompt with the same
woman, man, and location as the hardcore prompt. woman, man, and location as the hardcore prompt. In that setup, the softcore
prompt keeps the same listed adult cast physically present together in a
non-explicit teaser pose, with deterministic non-explicit partner outfits and a
shared cast pose.
It outputs: It outputs:
+19 -2
View File
@@ -485,17 +485,34 @@ def _insta_of_pair_from_row(row: dict[str, Any], detail_level: str, keep_style:
soft_text, _soft_method = _metadata_to_prose(soft_row, detail_level, keep_style) soft_text, _soft_method = _metadata_to_prose(soft_row, detail_level, keep_style)
hard_text, _hard_method = _metadata_to_prose(hard_row_for_text, detail_level, keep_style) hard_text, _hard_method = _metadata_to_prose(hard_row_for_text, detail_level, keep_style)
descriptor = _clean_text(row.get("shared_descriptor")) descriptor = _clean_text(row.get("shared_descriptor"))
options = row.get("options") if isinstance(row.get("options"), dict) else {}
cast_descriptors = row.get("shared_cast_descriptors") cast_descriptors = row.get("shared_cast_descriptors")
if isinstance(cast_descriptors, list): if isinstance(cast_descriptors, list):
cast_descriptor_text = _human_join([_clean_text(item) for item in cast_descriptors if _clean_text(item)]) cast_descriptor_text = _human_join([_clean_text(item) for item in cast_descriptors if _clean_text(item)])
else: else:
cast_descriptor_text = _clean_text(cast_descriptors) cast_descriptor_text = _clean_text(cast_descriptors)
same_soft_cast = options.get("softcore_cast") == "same_as_hardcore"
parts = [] parts = []
if cast_descriptor_text: if cast_descriptor_text and same_soft_cast:
parts.append(f"The shared cast descriptors are {cast_descriptor_text}") parts.append(f"The shared cast descriptors are {cast_descriptor_text}")
elif descriptor: elif descriptor:
parts.append(f"The shared creator descriptor is {descriptor}") parts.append(f"The softcore primary creator descriptor is {descriptor}")
if cast_descriptor_text and not same_soft_cast:
parts.append(f"The hardcore cast descriptors are {cast_descriptor_text}")
if same_soft_cast:
parts.append("The softcore version keeps the same adult cast present together in a non-explicit teaser setup")
partner_styling = row.get("softcore_partner_styling")
if isinstance(partner_styling, dict):
outfits = partner_styling.get("outfits")
if isinstance(outfits, list):
outfit_text = _human_join([_clean_text(item) for item in outfits if _clean_text(item)])
if outfit_text:
parts.append(f"Softcore partner styling: {outfit_text}")
pose = _clean_text(partner_styling.get("pose"))
if pose:
parts.append(f"The shared softcore cast pose is {pose}")
if soft_text: if soft_text:
parts.append(f"Softcore version: {soft_text}") parts.append(f"Softcore version: {soft_text}")
if hard_text: if hard_text:
+14 -3
View File
@@ -986,6 +986,10 @@
"bed-level" "bed-level"
], ],
"body_contact": [ "body_contact": [
"one hand gripping the sheets",
"hips lifted toward the camera",
"body arched on rumpled sheets",
"thighs tense against the bedding",
"bodies still pressed together", "bodies still pressed together",
"hands gripping hips", "hands gripping hips",
"thighs spread open", "thighs spread open",
@@ -1015,7 +1019,8 @@
}, },
{ {
"text": "wet orgasm during strap-on penetration", "text": "wet orgasm during strap-on penetration",
"cast": "women_only" "cast": "women_only",
"min_people": 2
}, },
{ {
"text": "post-orgasm dripping arousal", "text": "post-orgasm dripping arousal",
@@ -1024,10 +1029,16 @@
"external cumshot", "external cumshot",
"visible external ejaculation", "visible external ejaculation",
"messy post-climax release", "messy post-climax release",
"orgasm during penetration", {
"text": "orgasm during penetration",
"min_people": 2
},
"post-orgasm hardcore climax", "post-orgasm hardcore climax",
"visible orgasm aftermath", "visible orgasm aftermath",
"shared climax after penetration", {
"text": "shared climax after penetration",
"min_people": 2
},
"hardcore ejaculation scene" "hardcore ejaculation scene"
], ],
"expression_detail": [ "expression_detail": [
+20 -2
View File
@@ -299,10 +299,28 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
if options.get("softcore_cast") == "same_as_hardcore" if options.get("softcore_cast") == "same_as_hardcore"
else f"Woman A / primary creator: {descriptor}" else f"Woman A / primary creator: {descriptor}"
) )
same_soft_cast = options.get("softcore_cast") == "same_as_hardcore"
soft_cast_presence = (
"The same listed adult cast is present together in this softcore version in a non-explicit teaser pose, with no sex act or genital contact"
if same_soft_cast
else "The softcore version focuses on Woman A alone"
)
partner_styling = row.get("softcore_partner_styling")
if isinstance(partner_styling, dict):
outfits = partner_styling.get("outfits")
partner_outfit_text = "; ".join(_clean(item) for item in outfits if _clean(item)) if isinstance(outfits, list) else ""
partner_pose = _clean(partner_styling.get("pose"))
else:
partner_outfit_text = ""
partner_pose = ""
soft_parts = [ soft_parts = [
f"A visibly adult creator, {descriptor}", f"A visibly adult creator, {descriptor}",
f"Shared cast descriptors: {soft_cast_descriptor_text}" if soft_cast_descriptor_text else "", f"Shared cast descriptors: {soft_cast_descriptor_text}" if same_soft_cast and soft_cast_descriptor_text else "",
f"Softcore primary creator descriptor: {soft_cast_descriptor_text}" if not same_soft_cast and soft_cast_descriptor_text else "",
soft_cast_presence,
f"Partner softcore styling: {partner_outfit_text}" if partner_outfit_text else "",
f"The shared softcore cast pose is {partner_pose}" if partner_pose else "",
f"shown in a {soft_level or 'softcore'} Insta/OF creator image", f"shown in a {soft_level or 'softcore'} Insta/OF creator image",
f"wearing {soft.get('item')}" if soft.get("item") else "", f"wearing {soft.get('item')}" if soft.get("item") else "",
f"{soft.get('pose')}" if soft.get("pose") else "", f"{soft.get('pose')}" if soft.get("pose") else "",
@@ -314,7 +332,7 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
] ]
hard_parts = [ hard_parts = [
f"The same visibly adult creator, {descriptor}, is the visually central woman in a consensual explicit adult {hard_level or 'hardcore'} scene", f"The same visibly adult creator, {descriptor}, is the visually central woman in a consensual explicit adult {hard_level or 'hardcore'} scene",
f"Shared cast descriptors: {cast_descriptor_text}" if cast_descriptor_text else "", f"{'Shared' if same_soft_cast else 'Hardcore'} cast descriptors: {cast_descriptor_text}" if cast_descriptor_text else "",
f"all participants are 21+ and visibly adult; the cast includes {hard_cast_text}" if hard_cast_text else "all participants are 21+ and visibly adult", f"all participants are 21+ and visibly adult; the cast includes {hard_cast_text}" if hard_cast_text else "all participants are 21+ and visibly adult",
_clean(hard.get("role_graph")), _clean(hard.get("role_graph")),
f"The sexual action is {hard.get('item')}" if hard.get("item") else "", f"The sexual action is {hard.get('item')}" if hard.get("item") else "",
+105 -5
View File
@@ -343,6 +343,29 @@ def _heuristic_cast_compatible(text: str, women_count: int, men_count: int) -> b
if not text: if not text:
return True return True
total = women_count + men_count total = women_count + men_count
if total == 1:
solo_blocked_terms = (
"partner",
"partners",
"two bodies",
"three bodies",
"bodies still pressed",
"bodies pressed",
"bodies tangled",
"wet bodies",
"chests heaving together",
"straddling a partner",
"shared climax",
"between two",
"from both sides",
"front-and-back",
"body contact",
)
if any(term in text for term in solo_blocked_terms):
return False
solo_toy_terms = ("toy", "dildo", "finger", "fingers", "self")
if "penetration" in text and not any(term in text for term in solo_toy_terms):
return False
if total < 3 and "threesome" in text: if total < 3 and "threesome" in text:
return False return False
if total != 3 and ("centered threesome" in text or "three-way" in text): if total != 3 and ("centered threesome" in text or "three-way" in text):
@@ -395,6 +418,7 @@ def _heuristic_cast_compatible(text: str, women_count: int, men_count: int) -> b
"blowjob", "blowjob",
"fellatio", "fellatio",
"deepthroat", "deepthroat",
"ejaculation",
"semen", "semen",
) )
if any(term in text for term in male_terms) and not any(term in text for term in toy_terms): if any(term in text for term in male_terms) and not any(term in text for term in toy_terms):
@@ -1270,6 +1294,16 @@ def _role_graph(
] ]
return f" {extra} {rng.choice(actions)}." return f" {extra} {rng.choice(actions)}."
if people_count == 1:
solo = people[0]
if women_count == 1:
if "cumshot" in slug or "climax" in slug:
return f"{solo} is shown in a solo explicit climax pose with thighs open, one hand on her body, and visible arousal on skin and sheets."
return f"{solo} is shown in a solo explicit adult pose with self-touch, open body framing, and direct camera awareness."
if "cumshot" in slug or "climax" in slug:
return f"{solo} is shown in a solo explicit climax pose with one hand on his cock, body angled toward the camera, and visible ejaculation detail."
return f"{solo} is shown in a solo explicit adult pose with direct camera awareness and clear body framing."
if women_count > 0 and men_count == 0: if women_count > 0 and men_count == 0:
a, b = _pick_distinct(rng, women, 2) a, b = _pick_distinct(rng, women, 2)
c = any_woman({a, b}) if len(women) >= 3 else "" c = any_woman({a, b}) if len(women) >= 3 else ""
@@ -2074,6 +2108,40 @@ def _insta_of_cast_phrase(women_count: int, men_count: int) -> str:
return context["cast_summary"] return context["cast_summary"]
SOFTCORE_CAST_POSES = [
"standing together for a mirror selfie with bodies close but no sexual contact",
"posing shoulder-to-shoulder in a creator-shot group teaser",
"leaning together on the bed in a non-explicit subscriber preview",
"sitting close together with hands kept above clothing",
"arranged around Woman A in a flirtatious non-explicit teaser pose",
"posing in the same room as a coordinated adult creator set",
"standing near the phone tripod with relaxed teasing body language",
"framed together in a softcore cast reveal with no sex act",
]
def _insta_of_partner_styling(
seed_config: dict[str, int],
seed: int,
row_number: int,
women_count: int,
men_count: int,
) -> dict[str, Any]:
content_rng = _axis_rng(seed_config, "content", seed, row_number + 421)
pose_rng = _axis_rng(seed_config, "pose", seed, row_number + 421)
outfits: list[str] = []
for index in range(max(0, women_count - 1)):
label = chr(ord("B") + index)
outfits.append(f"Woman {label} wears {g.choose(content_rng, g.WOMEN_CLOTHES_MINIMAL)}")
for index in range(max(0, men_count)):
label = chr(ord("A") + index)
outfits.append(f"Man {label} wears {g.choose(content_rng, g.MEN_CLOTHES_MINIMAL)}")
return {
"outfits": outfits,
"pose": g.choose(pose_rng, SOFTCORE_CAST_POSES),
}
def _insta_of_active_trigger(prompt: str, trigger: str, enabled: bool) -> str: def _insta_of_active_trigger(prompt: str, trigger: str, enabled: bool) -> str:
return _prepend_trigger(prompt, trigger, enabled) return _prepend_trigger(prompt, trigger, enabled)
@@ -2167,6 +2235,16 @@ def build_insta_of_pair(
if options["softcore_cast"] == "same_as_hardcore" if options["softcore_cast"] == "same_as_hardcore"
else f"Woman A / primary creator: {descriptor}" else f"Woman A / primary creator: {descriptor}"
) )
soft_partner_styling = _insta_of_partner_styling(
parsed_seed_config,
seed,
row_number,
hard_women_count if options["softcore_cast"] == "same_as_hardcore" else 1,
hard_men_count if options["softcore_cast"] == "same_as_hardcore" else 0,
)
if options["softcore_cast"] != "same_as_hardcore":
soft_partner_styling = {"outfits": [], "pose": ""}
soft_partner_outfit_text = "; ".join(soft_partner_styling["outfits"])
platform_style = INSTA_OF_PLATFORM_STYLES[options["platform_style"]] platform_style = INSTA_OF_PLATFORM_STYLES[options["platform_style"]]
soft_level = INSTA_OF_SOFT_LEVELS[options["softcore_level"]] soft_level = INSTA_OF_SOFT_LEVELS[options["softcore_level"]]
hard_level = INSTA_OF_HARDCORE_LEVELS[options["hardcore_level"]] hard_level = INSTA_OF_HARDCORE_LEVELS[options["hardcore_level"]]
@@ -2186,12 +2264,24 @@ def build_insta_of_pair(
if options["softcore_cast"] == "solo" if options["softcore_cast"] == "solo"
else f"non-explicit teaser setup with the same adult cast as the hardcore version: {_insta_of_cast_phrase(hard_women_count, hard_men_count)}" else f"non-explicit teaser setup with the same adult cast as the hardcore version: {_insta_of_cast_phrase(hard_women_count, hard_men_count)}"
) )
soft_cast_presence = (
"Show the same listed adult cast together in the softcore version, present in the same location in a non-explicit teaser pose with no sex act or genital contact. "
if options["softcore_cast"] == "same_as_hardcore"
else "Keep the softcore version focused on Woman A alone. "
)
soft_cast_styling_sentence = (
f"Partner softcore styling: {soft_partner_outfit_text}. Shared softcore cast pose: {soft_partner_styling['pose']}. "
if options["softcore_cast"] == "same_as_hardcore" and soft_partner_outfit_text
else ""
)
hard_cast = _insta_of_cast_phrase(hard_women_count, hard_men_count) hard_cast = _insta_of_cast_phrase(hard_women_count, hard_men_count)
soft_prompt = ( soft_prompt = (
f"Insta/OF softcore mode: {platform_style}. Shared primary creator descriptor: {descriptor}. " f"Insta/OF softcore mode: {platform_style}. Shared primary creator descriptor: {descriptor}. "
f"Softcore setup: {soft_level}. Cast continuity: {soft_cast}. " f"Softcore setup: {soft_level}. Cast continuity: {soft_cast}. "
f"Shared cast descriptors: {soft_cast_descriptor_text}. " f"Shared cast descriptors: {soft_cast_descriptor_text}. "
f"{soft_cast_presence}"
f"{soft_cast_styling_sentence}"
f"Outfit: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. " f"Outfit: {soft_row['item']}. Pose: {soft_row['pose']}. Setting: {soft_row['scene_text']}. "
f"Facial expression: {soft_row['expression']}. Composition: {soft_row['composition']}. " f"Facial expression: {soft_row['expression']}. Composition: {soft_row['composition']}. "
f"{soft_camera_sentence}" f"{soft_camera_sentence}"
@@ -2217,11 +2307,20 @@ def build_insta_of_pair(
hard_prompt = _insta_of_active_trigger(hard_prompt, active_trigger, bool(prepend_trigger_to_prompt)) hard_prompt = _insta_of_active_trigger(hard_prompt, active_trigger, bool(prepend_trigger_to_prompt))
soft_negative = _combined_negative(INSTA_OF_SOFT_NEGATIVE, extra_negative) soft_negative = _combined_negative(INSTA_OF_SOFT_NEGATIVE, extra_negative)
hard_negative = _combined_negative(INSTA_OF_NEGATIVE, extra_negative) hard_negative = _combined_negative(INSTA_OF_NEGATIVE, extra_negative)
soft_caption = ( soft_caption_parts = [
f"{active_trigger}, Insta/OF softcore mode, {descriptor}, {soft_level}, " active_trigger,
f"{soft_row['item']}, {soft_row['pose']}, {soft_row['scene_text']}, {soft_row['composition']}, " "Insta/OF softcore mode",
f"{soft_camera_config['camera_mode'].replace('_', ' ')} camera" descriptor,
) soft_level,
soft_row["item"],
soft_row["pose"],
soft_partner_outfit_text,
soft_partner_styling["pose"],
soft_row["scene_text"],
soft_row["composition"],
f"{soft_camera_config['camera_mode'].replace('_', ' ')} camera",
]
soft_caption = ", ".join(str(part).strip() for part in soft_caption_parts if str(part).strip())
hard_caption = ( hard_caption = (
f"{active_trigger}, Insta/OF hardcore mode, same primary creator descriptor, {descriptor}, " f"{active_trigger}, Insta/OF hardcore mode, same primary creator descriptor, {descriptor}, "
f"{hard_cast}, {hard_row['role_graph']}, {hard_row['item']}, {hard_scene}, {hard_composition}, " f"{hard_cast}, {hard_row['role_graph']}, {hard_row['item']}, {hard_scene}, {hard_composition}, "
@@ -2232,6 +2331,7 @@ def build_insta_of_pair(
"options": options, "options": options,
"shared_descriptor": descriptor, "shared_descriptor": descriptor,
"shared_cast_descriptors": cast_descriptors, "shared_cast_descriptors": cast_descriptors,
"softcore_partner_styling": soft_partner_styling,
"softcore_prompt": soft_prompt, "softcore_prompt": soft_prompt,
"hardcore_prompt": hard_prompt, "hardcore_prompt": hard_prompt,
"softcore_negative_prompt": soft_negative, "softcore_negative_prompt": soft_negative,