Make POV prompt restore affect Krea output
This commit is contained in:
@@ -72,10 +72,18 @@ def sanitize_hardcore_environment_anchors(value: Any) -> str:
|
|||||||
return text.strip()
|
return text.strip()
|
||||||
|
|
||||||
|
|
||||||
def sanitize_hardcore_axis_values(values: Any) -> dict[str, str]:
|
def sanitize_hardcore_axis_value(value: Any) -> Any:
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [sanitize_hardcore_axis_value(item) for item in value]
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return {str(key): sanitize_hardcore_axis_value(item) for key, item in value.items()}
|
||||||
|
return sanitize_hardcore_environment_anchors(value)
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_hardcore_axis_values(values: Any) -> dict[str, Any]:
|
||||||
if not isinstance(values, dict):
|
if not isinstance(values, dict):
|
||||||
return {}
|
return {}
|
||||||
return {
|
return {
|
||||||
str(key): sanitize_hardcore_environment_anchors(value)
|
str(key): sanitize_hardcore_axis_value(value)
|
||||||
for key, value in values.items()
|
for key, value in values.items()
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -6,7 +6,14 @@ from typing import Any
|
|||||||
|
|
||||||
PLACEHOLDER_VALUES = {"", "any", "auto", "random", "none", "null"}
|
PLACEHOLDER_VALUES = {"", "any", "auto", "random", "none", "null"}
|
||||||
PREFERRED_VALUE_KEYS = ("text", "prompt", "template", "value", "name")
|
PREFERRED_VALUE_KEYS = ("text", "prompt", "template", "value", "name")
|
||||||
METADATA_AXIS_KEYS = {"action_family", "position_family", "position_key", "position_keys", "krea2_variant_keys"}
|
METADATA_AXIS_KEYS = {
|
||||||
|
"action_family",
|
||||||
|
"position_family",
|
||||||
|
"position_key",
|
||||||
|
"position_keys",
|
||||||
|
"krea2_variant_keys",
|
||||||
|
"restored_prompt_axes",
|
||||||
|
}
|
||||||
ACTION_CONTEXT_PRIORITY = (
|
ACTION_CONTEXT_PRIORITY = (
|
||||||
"position",
|
"position",
|
||||||
"body_position",
|
"body_position",
|
||||||
@@ -14,6 +21,7 @@ ACTION_CONTEXT_PRIORITY = (
|
|||||||
"arrangement",
|
"arrangement",
|
||||||
"angle",
|
"angle",
|
||||||
"surface",
|
"surface",
|
||||||
|
"restored_prompt_details",
|
||||||
"body_contact",
|
"body_contact",
|
||||||
"leg_detail",
|
"leg_detail",
|
||||||
"outer_act",
|
"outer_act",
|
||||||
|
|||||||
+6
-1
@@ -110,7 +110,12 @@ def _krea2_atlas_variant_sentence(axis_values: Any) -> str:
|
|||||||
if not variant:
|
if not variant:
|
||||||
return ""
|
return ""
|
||||||
cues = _unique_texts(list(variant.get("prompt_cues") or []) or [variant.get("canonical_geometry")])
|
cues = _unique_texts(list(variant.get("prompt_cues") or []) or [variant.get("canonical_geometry")])
|
||||||
return _clean(". ".join(cues)).rstrip(".")
|
sentence = _clean(". ".join(cues)).rstrip(".")
|
||||||
|
if isinstance(axis_values, dict):
|
||||||
|
restored_details = _unique_texts(_list_values(axis_values.get("restored_prompt_details")))
|
||||||
|
if restored_details:
|
||||||
|
sentence = f"{sentence}. Additional visible detail: {'; '.join(restored_details)}"
|
||||||
|
return sentence
|
||||||
|
|
||||||
|
|
||||||
def pov_ejaculation_target(context: str) -> str:
|
def pov_ejaculation_target(context: str) -> str:
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ class SxCPKrea2POVPromptRestore:
|
|||||||
CLOTHING_AXES = ["clothing_detail"]
|
CLOTHING_AXES = ["clothing_detail"]
|
||||||
FACE_EXPRESSION_AXES = ["face_detail", "expression_detail", "mouth_detail", "reaction_detail"]
|
FACE_EXPRESSION_AXES = ["face_detail", "expression_detail", "mouth_detail", "reaction_detail"]
|
||||||
BODY_TOUCH_AXES = ["body_contact", "hand_detail", "touch_detail", "foreplay_detail"]
|
BODY_TOUCH_AXES = ["body_contact", "hand_detail", "touch_detail", "foreplay_detail"]
|
||||||
CAMERA_PRESENTATION_AXES = ["performance_act", "visibility", "angle"]
|
CAMERA_PRESENTATION_AXES = ["performance_act", "visibility"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
|
|||||||
@@ -327,6 +327,14 @@ NODE_INPUT_TOOLTIPS = {
|
|||||||
"combine_mode": "replace discards incoming position choices; add merges this variant with the incoming position config.",
|
"combine_mode": "replace discards incoming position choices; add merges this variant with the incoming position config.",
|
||||||
"hardcore_position_config": "Optional incoming hardcore position config. Connect this when layering a variant on an existing pool.",
|
"hardcore_position_config": "Optional incoming hardcore position config. Connect this when layering a variant on an existing pool.",
|
||||||
},
|
},
|
||||||
|
"SxCPKrea2POVPromptRestore": {
|
||||||
|
"restore_clothing_detail": "Let compatible clothing/body-exposure detail survive a strict Krea2 POV atlas pose lock when the source category has that axis.",
|
||||||
|
"restore_face_expression_detail": "Restore compatible face, expression, mouth, and reaction detail as visible prompt detail without changing the atlas pose.",
|
||||||
|
"restore_body_touch_detail": "Restore compatible body-contact, hand, touch, and foreplay detail as visible prompt detail while keeping the pose locked.",
|
||||||
|
"restore_camera_presentation_detail": "Restore compatible presentation and visibility wording. Camera angle axes stay locked to the atlas pose.",
|
||||||
|
"relax_non_pose_axis_conflicts": "Allow restored non-pose axes to pass position-conflict pruning. Position axes remain locked to the selected atlas pose.",
|
||||||
|
"hardcore_position_config": "Optional incoming Krea2 POV position config. Use before or after a POV filter to add restored prompt-detail axes.",
|
||||||
|
},
|
||||||
"SxCPKrea2VariantEvidence": {
|
"SxCPKrea2VariantEvidence": {
|
||||||
"variant_key": "Catalog variant whose fixed-seed eval evidence should be shown.",
|
"variant_key": "Catalog variant whose fixed-seed eval evidence should be shown.",
|
||||||
"result": "Filter eval entries by result. accepted is the evidence used for proven variants.",
|
"result": "Filter eval entries by result. accepted is the evidence used for proven variants.",
|
||||||
|
|||||||
@@ -100,6 +100,101 @@ class CategoryItemRoute:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _unique_texts(values: list[Any]) -> list[str]:
|
||||||
|
selected: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for value in values:
|
||||||
|
text = row_item_policy.entry_text(value).strip(" .;")
|
||||||
|
lower = text.lower()
|
||||||
|
if not text or lower in seen:
|
||||||
|
continue
|
||||||
|
selected.append(text)
|
||||||
|
seen.add(lower)
|
||||||
|
return selected
|
||||||
|
|
||||||
|
|
||||||
|
def _restore_axis_values_for_context(
|
||||||
|
values: list[Any],
|
||||||
|
*,
|
||||||
|
subcategory_slug: str,
|
||||||
|
axis_name: str,
|
||||||
|
item_axis_values: dict[str, Any],
|
||||||
|
women_count: int,
|
||||||
|
men_count: int,
|
||||||
|
) -> list[Any]:
|
||||||
|
values = category_policy.compatible_entries(values, women_count, men_count)
|
||||||
|
if subcategory_slug == "oral_sex":
|
||||||
|
return row_item_policy.oral_axis_values_for_context(
|
||||||
|
values,
|
||||||
|
str(item_axis_values.get("position") or ""),
|
||||||
|
str(item_axis_values.get("oral_act") or ""),
|
||||||
|
axis_name,
|
||||||
|
)
|
||||||
|
if subcategory_slug == "outercourse_sex":
|
||||||
|
return row_item_policy.outercourse_axis_values_for_position(
|
||||||
|
values,
|
||||||
|
str(item_axis_values.get("position") or ""),
|
||||||
|
axis_name,
|
||||||
|
)
|
||||||
|
if subcategory_slug == "anal_double_penetration":
|
||||||
|
return row_item_policy.anal_axis_values_for_position(
|
||||||
|
values,
|
||||||
|
str(item_axis_values.get("position") or ""),
|
||||||
|
axis_name,
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def _restored_prompt_axis_values(
|
||||||
|
rng: Any,
|
||||||
|
subcategory: dict[str, Any],
|
||||||
|
item_axis_values: dict[str, Any],
|
||||||
|
hardcore_position_config: dict[str, Any],
|
||||||
|
women_count: int,
|
||||||
|
men_count: int,
|
||||||
|
) -> dict[str, str]:
|
||||||
|
restore_axes = hardcore_position_policy.normalize_restore_prompt_axes(
|
||||||
|
hardcore_position_config.get("restore_prompt_axes") if isinstance(hardcore_position_config, dict) else []
|
||||||
|
)
|
||||||
|
raw_axes = subcategory.get("item_axes")
|
||||||
|
if not restore_axes or not isinstance(raw_axes, dict):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
restored: dict[str, str] = {}
|
||||||
|
subcategory_slug = str(subcategory.get("slug") or "").lower()
|
||||||
|
for axis_name in restore_axes:
|
||||||
|
existing = ""
|
||||||
|
if axis_name in item_axis_values and item_axis_values.get(axis_name) is not None:
|
||||||
|
existing = row_item_policy.entry_text(item_axis_values.get(axis_name)).strip(" .;")
|
||||||
|
if existing:
|
||||||
|
restored[axis_name] = existing
|
||||||
|
continue
|
||||||
|
values = _list_from(raw_axes.get(axis_name))
|
||||||
|
if not values:
|
||||||
|
continue
|
||||||
|
values = _restore_axis_values_for_context(
|
||||||
|
values,
|
||||||
|
subcategory_slug=subcategory_slug,
|
||||||
|
axis_name=axis_name,
|
||||||
|
item_axis_values=item_axis_values,
|
||||||
|
women_count=women_count,
|
||||||
|
men_count=men_count,
|
||||||
|
)
|
||||||
|
if not values:
|
||||||
|
continue
|
||||||
|
restored[axis_name] = row_item_policy.entry_text(row_item_policy.weighted_choice(rng, values)).strip(" .;")
|
||||||
|
return {axis: value for axis, value in restored.items() if value}
|
||||||
|
|
||||||
|
|
||||||
|
def _append_restored_prompt_details(item_text: str, details: list[str]) -> str:
|
||||||
|
details = [detail for detail in _unique_texts(details) if detail.lower() not in str(item_text or "").lower()]
|
||||||
|
if not details:
|
||||||
|
return item_text
|
||||||
|
if not item_text:
|
||||||
|
return "; ".join(details)
|
||||||
|
return f"{str(item_text).rstrip(' .')}, with {'; '.join(details)}"
|
||||||
|
|
||||||
|
|
||||||
def select_category_item_route_result(
|
def select_category_item_route_result(
|
||||||
*,
|
*,
|
||||||
category_choice: str,
|
category_choice: str,
|
||||||
@@ -159,6 +254,23 @@ def select_category_item_route_result(
|
|||||||
women_count,
|
women_count,
|
||||||
men_count,
|
men_count,
|
||||||
)
|
)
|
||||||
|
restored_axis_values = _restored_prompt_axis_values(
|
||||||
|
content_rng,
|
||||||
|
subcategory,
|
||||||
|
item_axis_values,
|
||||||
|
parsed_hardcore_position_config,
|
||||||
|
women_count,
|
||||||
|
men_count,
|
||||||
|
)
|
||||||
|
if restored_axis_values:
|
||||||
|
restored_details = _unique_texts(list(restored_axis_values.values()))
|
||||||
|
item_axis_values = {
|
||||||
|
**item_axis_values,
|
||||||
|
**restored_axis_values,
|
||||||
|
"restored_prompt_axes": list(restored_axis_values.keys()),
|
||||||
|
"restored_prompt_details": restored_details,
|
||||||
|
}
|
||||||
|
item_text = _append_restored_prompt_details(item_text, restored_details)
|
||||||
if is_pose_category:
|
if is_pose_category:
|
||||||
item_text = sanitize_hardcore_environment_anchors(item_text)
|
item_text = sanitize_hardcore_environment_anchors(item_text)
|
||||||
item_axis_values = sanitize_hardcore_axis_values(item_axis_values)
|
item_axis_values = sanitize_hardcore_axis_values(item_axis_values)
|
||||||
|
|||||||
@@ -8442,6 +8442,63 @@ def smoke_pov_oral_position_routes() -> None:
|
|||||||
_expect("eye-level shot" not in top_prompt, f"Top-view variant prompt kept contradictory eye-level camera text: {top_prompt}")
|
_expect("eye-level shot" not in top_prompt, f"Top-view variant prompt kept contradictory eye-level camera text: {top_prompt}")
|
||||||
_expect("tongue extended toward genitals" not in top_prompt, f"Top-view variant prompt kept contradictory tongue-extension expression: {top_prompt}")
|
_expect("tongue extended toward genitals" not in top_prompt, f"Top-view variant prompt kept contradictory tongue-extension expression: {top_prompt}")
|
||||||
|
|
||||||
|
restored_top_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVPromptRestore"]().build(
|
||||||
|
True,
|
||||||
|
True,
|
||||||
|
True,
|
||||||
|
True,
|
||||||
|
True,
|
||||||
|
top_variant_config,
|
||||||
|
)[0]
|
||||||
|
restored_top_pair = pb.build_insta_of_pair(
|
||||||
|
row_number=1,
|
||||||
|
start_index=1,
|
||||||
|
seed=3828,
|
||||||
|
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=restored_top_config,
|
||||||
|
location_config=_coworking_location_config(),
|
||||||
|
hardcore_camera_config=_orbit_camera(
|
||||||
|
horizontal_angle=45,
|
||||||
|
vertical_angle=0,
|
||||||
|
zoom=7.5,
|
||||||
|
subject_focus="action",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
restored_top_row = restored_top_pair.get("hardcore_row") or {}
|
||||||
|
restored_top_axis = restored_top_row.get("item_axis_values") or {}
|
||||||
|
restored_details = restored_top_axis.get("restored_prompt_details") or []
|
||||||
|
_expect(isinstance(restored_details, list), "Krea2 POV Prompt Restore should keep restored prompt details as a list")
|
||||||
|
_expect(restored_details, "Krea2 POV Prompt Restore should add sampled restored axis details to row metadata")
|
||||||
|
restored_top_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(restored_top_pair), target="hardcore")
|
||||||
|
restored_top_prompt = _expect_text("pov_oral_top_view_restore.krea_prompt", restored_top_krea.get("krea_prompt"), 60).lower()
|
||||||
|
_expect(
|
||||||
|
restored_top_prompt != top_prompt,
|
||||||
|
"Krea2 POV Prompt Restore should visibly change the final Krea prompt, not only metadata",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
"nadir-angle standing male pov top-view oral position" in restored_top_prompt,
|
||||||
|
"Krea2 POV Prompt Restore should preserve the exact atlas pose while adding detail",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
"eye-level" not in restored_top_prompt,
|
||||||
|
f"Krea2 POV Prompt Restore should not reintroduce contradictory eye-level camera wording: {restored_top_prompt}",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
any(str(detail).lower() in restored_top_prompt for detail in restored_details),
|
||||||
|
f"Krea2 POV Prompt Restore final prompt did not include sampled restored details: {restored_top_prompt}",
|
||||||
|
)
|
||||||
|
|
||||||
sitting_variant_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVOralFilter"]().build(
|
sitting_variant_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVOralFilter"]().build(
|
||||||
"replace",
|
"replace",
|
||||||
"",
|
"",
|
||||||
|
|||||||
Reference in New Issue
Block a user