Move pair detail density policy

This commit is contained in:
2026-06-27 03:15:49 +02:00
parent 7f808be997
commit 9ca2320df2
5 changed files with 47 additions and 14 deletions
+3 -2
View File
@@ -214,8 +214,9 @@ Already isolated:
- Insta/OF option normalization, softcore category/outfit/pose pools, partner - Insta/OF option normalization, softcore category/outfit/pose pools, partner
outfit pools, clothing-continuity labels, negatives, and hardcore cast count outfit pools, clothing-continuity labels, negatives, and hardcore cast count
policy live in `pair_options.py`; `prompt_builder.py` keeps public delegate policy, plus hardcore detail-density directive text, live in
wrappers for existing nodes and tests. `pair_options.py`; `prompt_builder.py` keeps public delegate wrappers for
existing nodes and tests.
- soft/hard row creation lives in `pair_rows.py`, including softcore expression - soft/hard row creation lives in `pair_rows.py`, including softcore expression
override resolution, Woman A slot context application, soft outfit/pose override resolution, Woman A slot context application, soft outfit/pose
overrides, POV row fields, and hardcore row creation. overrides, POV row fields, and hardcore row creation.
+3 -3
View File
@@ -79,7 +79,7 @@ Core helper ownership:
| `location_config.py` | Location/composition preset schemas, themed location packs, custom location/composition parsing, pool merge behavior, and location/composition config parsing. | | `location_config.py` | Location/composition preset schemas, themed location packs, custom location/composition parsing, pool merge behavior, and location/composition config parsing. |
| `row_location.py` | Built-in row location/composition config application, deterministic scene/composition choice, source metadata, and legacy prompt/caption rewrites. | | `row_location.py` | Built-in row location/composition config application, deterministic scene/composition choice, source metadata, and legacy prompt/caption rewrites. |
| `hardcore_position_config.py` | Hardcore position/action-filter choices, selected-position normalization, config JSON builders/parsers, focus-policy toggles, subcategory allow-list policy, position-key detection, and category/template/axis filtering. | | `hardcore_position_config.py` | Hardcore position/action-filter choices, selected-position normalization, config JSON builders/parsers, focus-policy toggles, subcategory allow-list policy, position-key detection, and category/template/axis filtering. |
| `pair_options.py` | Insta/OF option schema/defaults, softcore category/outfit/pose pools, partner outfit pools, clothing-continuity labels, negatives, and hardcore cast count policy. | | `pair_options.py` | Insta/OF option schema/defaults, softcore category/outfit/pose pools, partner outfit pools, clothing-continuity labels, negatives, hardcore cast count policy, and hardcore detail-density directives. |
| `pair_rows.py` | Insta/OF soft/hard row creation, softcore expression override resolution, Woman A slot context application, soft outfit/pose overrides, and POV row fields. | | `pair_rows.py` | Insta/OF soft/hard row creation, softcore expression override resolution, Woman A slot context application, soft outfit/pose overrides, and POV row fields. |
| `pair_camera.py` | Insta/OF soft/hard camera route resolution, same-as-softcore camera mode, camera-detail override, camera-aware composition mutation, POV camera suppression, and synchronized row/root camera metadata. | | `pair_camera.py` | Insta/OF soft/hard camera route resolution, same-as-softcore camera mode, camera-detail override, camera-aware composition mutation, POV camera suppression, and synchronized row/root camera metadata. |
| `pair_clothing.py` | Insta/OF hardcore clothing continuity, action-aware body-access flags, conflicting outfit-piece cleanup, default visible-men clothing, and final root clothing-state assembly. | | `pair_clothing.py` | Insta/OF hardcore clothing continuity, action-aware body-access flags, conflicting outfit-piece cleanup, default visible-men clothing, and final root clothing-state assembly. |
@@ -261,7 +261,7 @@ This table is the first stop when the selected content is wrong.
| `sexual_poses.json` foreplay/interaction/manual/oral/outercourse/penetration/etc. | Hardcore action and porn-scene interaction templates, role graphs, axis values, hardcore pool references | `pose` for pose-content route, also `role`; sometimes `content` aliases matter | High because Krea2 rewrites action and POV position text | | `sexual_poses.json` foreplay/interaction/manual/oral/outercourse/penetration/etc. | Hardcore action and porn-scene interaction templates, role graphs, axis values, hardcore pool references | `pose` for pose-content route, also `role`; sometimes `content` aliases matter | High because Krea2 rewrites action and POV position text |
| `location_pools.json` | Reusable scene pools and legacy scene extensions | `scene` | Medium when a camera-aware adapter changes scene/composition wording | | `location_pools.json` | Reusable scene pools and legacy scene extensions | `scene` | Medium when a camera-aware adapter changes scene/composition wording |
| `expression_composition_pools.json` | Reusable expressions and framing/composition pools | `expression`, `composition` | Medium because formatter may label or suppress expressions | | `expression_composition_pools.json` | Reusable expressions and framing/composition pools | `expression`, `composition` | Medium because formatter may label or suppress expressions |
| `pair_options.py` | Insta/OF option defaults, softcore level-to-category mapping, creator outfit/pose pools, partner outfit pools, negatives, and hard cast count policy | Options node plus `content`/`pose` axes inside pair route | Medium because pair route pools must remain consistent with Krea/SDXL pair formatting | | `pair_options.py` | Insta/OF option defaults, softcore level-to-category mapping, creator outfit/pose pools, partner outfit pools, negatives, hard cast count policy, and hardcore detail-density directives | Options node plus `content`/`pose` axes inside pair route | Medium because pair route pools must remain consistent with Krea/SDXL pair formatting |
| `generate_prompt_batches.py` legacy pools | Built-in generator clothing, pose, expression, scene, composition lists | Main row seed plus axis config through legacy adapter | Medium because legacy prompt format is field-label heavy | | `generate_prompt_batches.py` legacy pools | Built-in generator clothing, pose, expression, scene, composition lists | Main row seed plus axis config through legacy adapter | Medium because legacy prompt format is field-label heavy |
When adding a new pool, choose JSON when the change is pure selectable wording. When adding a new pool, choose JSON when the change is pure selectable wording.
@@ -503,7 +503,7 @@ plain prompt text. When debugging, inspect these fields before editing pools.
| `character_hardcore_clothing` | Character slots | Krea pair branch | Explicit per-character hardcore clothing state. | | `character_hardcore_clothing` | Character slots | Krea pair branch | Explicit per-character hardcore clothing state. |
| `default_man_hardcore_clothing` | Pair fallback | Krea pair branch | Auto clothing for visible men without configured clothing. | | `default_man_hardcore_clothing` | Pair fallback | Krea pair branch | Auto clothing for visible men without configured clothing. |
| `hardcore_clothing_state` | Pair clothing continuity | Krea/SDXL pair branch | Final hard clothing/body exposure sentence before Krea cleanup. | | `hardcore_clothing_state` | Pair clothing continuity | Krea/SDXL pair branch | Final hard clothing/body exposure sentence before Krea cleanup. |
| `hardcore_detail_density` | Insta/OF options | Krea hardcore action rewrite | Controls compact/balanced/dense action detail. | | `hardcore_detail_density` | Insta/OF options via `pair_options.py` | Krea hardcore action rewrite | Controls compact/balanced/dense action detail directives. |
| `softcore_camera_config`, `hardcore_camera_config` | Pair camera route | Krea/SDXL pair branch | Separate camera configs after option mode resolution. | | `softcore_camera_config`, `hardcore_camera_config` | Pair camera route | Krea/SDXL pair branch | Separate camera configs after option mode resolution. |
| `softcore_camera_directive`, `hardcore_camera_directive` | Pair camera route | Krea pair branch | Separate plain camera sentences, suppressed for POV. | | `softcore_camera_directive`, `hardcore_camera_directive` | Pair camera route | Krea pair branch | Separate plain camera sentences, suppressed for POV. |
| `softcore_camera_scene_directive`, `hardcore_camera_scene_directive` | Scene-camera adapter | Krea/Naturalizer pair branch | Separate location-aware camera layout text. | | `softcore_camera_scene_directive`, `hardcore_camera_scene_directive` | Scene-camera adapter | Krea/Naturalizer pair branch | Separate location-aware camera layout text. |
+18 -2
View File
@@ -32,6 +32,14 @@ INSTA_OF_HARDCORE_CLOTHING_CONTINUITY = {
"explicit_nude": "Woman A's body is fully exposed, bare skin unobstructed", "explicit_nude": "Woman A's body is fully exposed, bare skin unobstructed",
} }
HARDCORE_DETAIL_DENSITY_CHOICES = ["compact", "balanced", "dense"]
HARDCORE_DETAIL_DIRECTIVES = {
"compact": "Use one compact position-first sexual action sentence; avoid repeated aftermath wording. ",
"balanced": "",
"dense": "Use dense but coherent motion, contact, and aftermath detail while keeping one readable body position. ",
}
INSTA_OF_NEGATIVE = ( INSTA_OF_NEGATIVE = (
"minors, childlike appearance, teen, underage, schoolgirl, non-consensual, coercion, rape, " "minors, childlike appearance, teen, underage, schoolgirl, non-consensual, coercion, rape, "
"violence, injury, blood, gore, incest, bestiality, watermark, logo, readable username, social media UI" "violence, injury, blood, gore, incest, bestiality, watermark, logo, readable username, social media UI"
@@ -218,6 +226,14 @@ def character_softcore_outfit_values(source: str, custom_outfits: str = "") -> l
return [] return []
def hardcore_detail_density_choices() -> list[str]:
return list(HARDCORE_DETAIL_DENSITY_CHOICES)
def hardcore_detail_directive(density: Any) -> str:
return HARDCORE_DETAIL_DIRECTIVES.get(str(density or "balanced"), "")
def character_hardcore_clothing_values(state: str, custom_clothing: str = "") -> list[str]: def character_hardcore_clothing_values(state: str, custom_clothing: str = "") -> list[str]:
state = str(state or "no_change").strip() state = str(state or "no_change").strip()
if state == "fully_nude": if state == "fully_nude":
@@ -251,7 +267,7 @@ def build_insta_of_options_json(
softcore_expression_enabled: bool = True, softcore_expression_enabled: bool = True,
hardcore_expression_enabled: bool = True, hardcore_expression_enabled: bool = True,
hardcore_detail_density: str = "balanced", hardcore_detail_density: str = "balanced",
hardcore_detail_density_choices: list[str] | tuple[str, ...] = ("compact", "balanced", "dense"), hardcore_detail_density_choices: list[str] | tuple[str, ...] = tuple(HARDCORE_DETAIL_DENSITY_CHOICES),
) -> str: ) -> str:
hardcore_detail_density = ( hardcore_detail_density = (
hardcore_detail_density if hardcore_detail_density in hardcore_detail_density_choices else "balanced" hardcore_detail_density if hardcore_detail_density in hardcore_detail_density_choices else "balanced"
@@ -286,7 +302,7 @@ def parse_insta_of_options(
*, *,
camera_mode_choices: dict[str, str] | list[str] | tuple[str, ...], camera_mode_choices: dict[str, str] | list[str] | tuple[str, ...],
camera_detail_choices: list[str] | tuple[str, ...], camera_detail_choices: list[str] | tuple[str, ...],
hardcore_detail_density_choices: list[str] | tuple[str, ...], hardcore_detail_density_choices: list[str] | tuple[str, ...] = tuple(HARDCORE_DETAIL_DENSITY_CHOICES),
) -> dict[str, Any]: ) -> dict[str, Any]:
defaults = { defaults = {
"softcore_cast": "solo", "softcore_cast": "solo",
+3 -7
View File
@@ -132,7 +132,7 @@ CHARACTER_HAIR_STYLE_CHOICES = character_policy.CHARACTER_HAIR_STYLE_CHOICES
CHARACTER_EYE_COLOR_CHOICES = character_policy.CHARACTER_EYE_COLOR_CHOICES CHARACTER_EYE_COLOR_CHOICES = character_policy.CHARACTER_EYE_COLOR_CHOICES
CAMERA_DETAIL_CHOICES = camera_policy.CAMERA_DETAIL_CHOICES CAMERA_DETAIL_CHOICES = camera_policy.CAMERA_DETAIL_CHOICES
HARDCORE_DETAIL_DENSITY_CHOICES = ["compact", "balanced", "dense"] HARDCORE_DETAIL_DENSITY_CHOICES = pair_options.HARDCORE_DETAIL_DENSITY_CHOICES
HARDCORE_POSITION_FAMILY_CHOICES = hardcore_position_policy.HARDCORE_POSITION_FAMILY_CHOICES HARDCORE_POSITION_FAMILY_CHOICES = hardcore_position_policy.HARDCORE_POSITION_FAMILY_CHOICES
HARDCORE_POSITION_FOCUS_CHOICES = hardcore_position_policy.HARDCORE_POSITION_FOCUS_CHOICES HARDCORE_POSITION_FOCUS_CHOICES = hardcore_position_policy.HARDCORE_POSITION_FOCUS_CHOICES
HARDCORE_POSITION_KEY_CHOICES = hardcore_position_policy.HARDCORE_POSITION_KEY_CHOICES HARDCORE_POSITION_KEY_CHOICES = hardcore_position_policy.HARDCORE_POSITION_KEY_CHOICES
@@ -1244,7 +1244,7 @@ def camera_detail_choices() -> list[str]:
def hardcore_detail_density_choices() -> list[str]: def hardcore_detail_density_choices() -> list[str]:
return list(HARDCORE_DETAIL_DENSITY_CHOICES) return pair_options.hardcore_detail_density_choices()
def hardcore_position_family_choices() -> list[str]: def hardcore_position_family_choices() -> list[str]:
@@ -4279,11 +4279,7 @@ def build_insta_of_pair(
hard_row["source_scene_text"] = hard_row.get("source_scene_text") or hard_row.get("scene_text", "") hard_row["source_scene_text"] = hard_row.get("source_scene_text") or hard_row.get("scene_text", "")
hard_row["scene_text"] = hard_scene hard_row["scene_text"] = hard_scene
hard_detail_density = options["hardcore_detail_density"] hard_detail_density = options["hardcore_detail_density"]
hard_detail_directive = { hard_detail_directive = pair_options.hardcore_detail_directive(hard_detail_density)
"compact": "Use one compact position-first sexual action sentence; avoid repeated aftermath wording. ",
"balanced": "",
"dense": "Use dense but coherent motion, contact, and aftermath detail while keeping one readable body position. ",
}[hard_detail_density]
pov_directive = _pov_prompt_directive(pov_character_labels) pov_directive = _pov_prompt_directive(pov_character_labels)
soft_descriptor_sentence = cast_context["soft_descriptor_sentence"] soft_descriptor_sentence = cast_context["soft_descriptor_sentence"]
+20
View File
@@ -1597,6 +1597,26 @@ def smoke_pair_options_policy() -> None:
pb.INSTA_OF_SOFTCORE_OUTFITS is pb.pair_options.INSTA_OF_SOFTCORE_OUTFITS, pb.INSTA_OF_SOFTCORE_OUTFITS is pb.pair_options.INSTA_OF_SOFTCORE_OUTFITS,
"prompt_builder should delegate Insta/OF softcore outfit policy to pair_options", "prompt_builder should delegate Insta/OF softcore outfit policy to pair_options",
) )
_expect(
pb.HARDCORE_DETAIL_DENSITY_CHOICES is pb.pair_options.HARDCORE_DETAIL_DENSITY_CHOICES,
"prompt_builder should delegate hardcore detail density choices to pair_options",
)
_expect(
pb.pair_options.hardcore_detail_directive("compact").startswith("Use one compact"),
"compact hardcore detail density should have a compact directive",
)
_expect(
pb.pair_options.hardcore_detail_directive("dense").startswith("Use dense"),
"dense hardcore detail density should have a dense directive",
)
_expect(
pb.pair_options.hardcore_detail_directive("balanced") == "",
"balanced hardcore detail density should not add a directive",
)
_expect(
pb.pair_options.hardcore_detail_directive("bad") == "",
"invalid hardcore detail density directive should be empty",
)
options = json.loads( options = json.loads(
pb.build_insta_of_options_json( pb.build_insta_of_options_json(
softcore_expression_enabled="false", softcore_expression_enabled="false",