Inherit hardcore template metadata
This commit is contained in:
@@ -92,6 +92,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 0.75,
|
"weight": 0.75,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "foreplay"
|
||||||
|
},
|
||||||
"item_label": "Foreplay action",
|
"item_label": "Foreplay action",
|
||||||
"positive_suffix": "Use clear adult body contact, readable hands and faces, visible undressing, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
"positive_suffix": "Use clear adult body contact, readable hands and faces, visible undressing, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Foreplay action: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult body contact, kissing, caressing, undressing, visible arousal, exposed skin, and readable hand placement. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Foreplay action: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult body contact, kissing, caressing, undressing, visible arousal, exposed skin, and readable hand placement. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
@@ -211,6 +215,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 0.85,
|
"weight": 0.85,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "manual"
|
||||||
|
},
|
||||||
"item_label": "Manual action",
|
"item_label": "Manual action",
|
||||||
"positive_suffix": "Use clear adult manual contact, readable hands, explicit body positioning, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
"positive_suffix": "Use clear adult manual contact, readable hands, explicit body positioning, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Manual action: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult manual stimulation, visible hands, exposed skin, clear body positioning, and readable reaction. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Manual action: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult manual stimulation, visible hands, exposed skin, clear body positioning, and readable reaction. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
@@ -316,6 +324,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 0.7,
|
"weight": 0.7,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "interaction"
|
||||||
|
},
|
||||||
"item_label": "Body interaction",
|
"item_label": "Body interaction",
|
||||||
"positive_suffix": "Use readable adult body contact, hands and mouth on skin, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
"positive_suffix": "Use readable adult body contact, hands and mouth on skin, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Body interaction: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult body worship, close skin contact, mouth and hand placement, exposed skin, and readable body positioning. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Body interaction: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult body worship, close skin contact, mouth and hand placement, exposed skin, and readable body positioning. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
@@ -425,6 +437,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 0.65,
|
"weight": 0.65,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "interaction"
|
||||||
|
},
|
||||||
"item_label": "Transition action",
|
"item_label": "Transition action",
|
||||||
"positive_suffix": "Use readable adult movement, clothing being moved, hands guiding bodies, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
"positive_suffix": "Use readable adult movement, clothing being moved, hands guiding bodies, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Transition action: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult undressing, position changes, visible hands, exposed skin, and clear movement from one sexual beat to the next. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Transition action: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult undressing, position changes, visible hands, exposed skin, and clear movement from one sexual beat to the next. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
@@ -530,6 +546,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 0.55,
|
"weight": 0.55,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "interaction"
|
||||||
|
},
|
||||||
"item_label": "Guidance action",
|
"item_label": "Guidance action",
|
||||||
"positive_suffix": "Use consensual adult control, readable hand placement, clear body positioning, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
"positive_suffix": "Use consensual adult control, readable hand placement, clear body positioning, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Guidance action: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through consensual adult guidance, hair or wrist control, body positioning, visible hands, exposed skin, and clear power dynamic. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Guidance action: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through consensual adult guidance, hair or wrist control, body positioning, visible hands, exposed skin, and clear power dynamic. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
@@ -640,6 +660,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 0.6,
|
"weight": 0.6,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "interaction"
|
||||||
|
},
|
||||||
"item_label": "Camera performance",
|
"item_label": "Camera performance",
|
||||||
"positive_suffix": "Use creator-shot adult presentation, readable camera-facing pose, exposed skin, clear hand placement, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
"positive_suffix": "Use creator-shot adult presentation, readable camera-facing pose, exposed skin, clear hand placement, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Camera performance: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through camera-aware adult presentation, body opened or displayed to the viewer, visible hands, exposed skin, and clean creator-shot framing. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Camera performance: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through camera-aware adult presentation, body opened or displayed to the viewer, visible hands, exposed skin, and clean creator-shot framing. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
@@ -744,6 +768,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 0.55,
|
"weight": 0.55,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "interaction"
|
||||||
|
},
|
||||||
"item_label": "Group interaction",
|
"item_label": "Group interaction",
|
||||||
"positive_suffix": "Use readable adult group coordination, clear body spacing, visible watching/touching roles, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
"positive_suffix": "Use readable adult group coordination, clear body spacing, visible watching/touching roles, exposed skin, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Group interaction: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult group coordination, watching, guiding hands, body presentation, exposed skin, and clear role spacing. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Group interaction: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult group coordination, watching, guiding hands, body presentation, exposed skin, and clear role spacing. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
@@ -846,6 +874,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 0.35,
|
"weight": 0.35,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "interaction"
|
||||||
|
},
|
||||||
"item_label": "Aftermath interaction",
|
"item_label": "Aftermath interaction",
|
||||||
"positive_suffix": "Use adult post-sex intimacy, readable bodies and hands, visible aftermath details, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
"positive_suffix": "Use adult post-sex intimacy, readable bodies and hands, visible aftermath details, warm erotic lighting, crisp comic linework, detailed hatching, and tactile textured paper.",
|
||||||
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Aftermath interaction: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult post-sex closeness, cleanup, visible skin, relaxed body contact, aftermath details, and readable hands and faces. {positive_suffix} Avoid: {negative_prompt}.",
|
"prompt_template": "{subject_phrase}, all 21+ consenting adults: {style}. Cast: {cast_summary}. Role graph: {role_graph} Aftermath interaction: {item}. Setting: {scene}. Composition: {composition}. Facial expressions: {expression}. Make the scene explicit through adult post-sex closeness, cleanup, visible skin, relaxed body contact, aftermath details, and readable hands and faces. {positive_suffix} Avoid: {negative_prompt}.",
|
||||||
@@ -950,6 +982,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "penetration",
|
||||||
|
"position_family": "penetrative"
|
||||||
|
},
|
||||||
"scene_pools": ["hardcore_penetrative_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
"scene_pools": ["hardcore_penetrative_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"expression_pools": ["hardcore_penetration_expressions"],
|
"expression_pools": ["hardcore_penetration_expressions"],
|
||||||
"composition_pools": ["penetration_compositions"],
|
"composition_pools": ["penetration_compositions"],
|
||||||
@@ -1123,6 +1159,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "oral",
|
||||||
|
"position_family": "oral"
|
||||||
|
},
|
||||||
"scene_pools": ["hardcore_oral_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
"scene_pools": ["hardcore_oral_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"expression_pools": ["hardcore_oral_expressions"],
|
"expression_pools": ["hardcore_oral_expressions"],
|
||||||
"composition_pools": ["oral_compositions"],
|
"composition_pools": ["oral_compositions"],
|
||||||
@@ -1266,6 +1306,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "outercourse",
|
||||||
|
"position_family": "outercourse"
|
||||||
|
},
|
||||||
"scene_pools": ["hardcore_private_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
"scene_pools": ["hardcore_private_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"expression_pools": ["hardcore_outercourse_expressions"],
|
"expression_pools": ["hardcore_outercourse_expressions"],
|
||||||
"compositions": [
|
"compositions": [
|
||||||
@@ -1420,6 +1464,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "default",
|
||||||
|
"position_family": "anal"
|
||||||
|
},
|
||||||
"scene_pools": ["hardcore_anal_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
"scene_pools": ["hardcore_anal_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"expression_pools": ["hardcore_anal_dp_expressions"],
|
"expression_pools": ["hardcore_anal_dp_expressions"],
|
||||||
"composition_pools": ["anal_dp_compositions"],
|
"composition_pools": ["anal_dp_compositions"],
|
||||||
@@ -1639,6 +1687,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "default",
|
||||||
|
"position_family": "threesome"
|
||||||
|
},
|
||||||
"scene_pools": ["hardcore_threesome_scenes", "hardcore_group_scenes", "hardcore_mirror_scenes"],
|
"scene_pools": ["hardcore_threesome_scenes", "hardcore_group_scenes", "hardcore_mirror_scenes"],
|
||||||
"expression_pools": ["hardcore_group_expressions"],
|
"expression_pools": ["hardcore_group_expressions"],
|
||||||
"composition_pools": ["threesome_compositions"],
|
"composition_pools": ["threesome_compositions"],
|
||||||
@@ -1822,6 +1874,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "default",
|
||||||
|
"position_family": "group"
|
||||||
|
},
|
||||||
"scene_pools": ["hardcore_group_scenes"],
|
"scene_pools": ["hardcore_group_scenes"],
|
||||||
"expression_pools": ["hardcore_group_expressions"],
|
"expression_pools": ["hardcore_group_expressions"],
|
||||||
"composition_pools": ["group_sex_compositions"],
|
"composition_pools": ["group_sex_compositions"],
|
||||||
@@ -1994,6 +2050,10 @@
|
|||||||
"inherit_expressions": false,
|
"inherit_expressions": false,
|
||||||
"inherit_compositions": false,
|
"inherit_compositions": false,
|
||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "climax",
|
||||||
|
"position_family": "climax"
|
||||||
|
},
|
||||||
"scene_pools": ["hardcore_climax_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
"scene_pools": ["hardcore_climax_scenes", "hardcore_bed_scenes", "hardcore_mirror_scenes"],
|
||||||
"expression_pools": ["hardcore_climax_expressions"],
|
"expression_pools": ["hardcore_climax_expressions"],
|
||||||
"composition_pools": ["climax_compositions"],
|
"composition_pools": ["climax_compositions"],
|
||||||
|
|||||||
@@ -34,6 +34,43 @@ def template_metadata(item: Any) -> dict[str, Any]:
|
|||||||
return {key: item[key] for key in TEMPLATE_METADATA_KEYS if key in item}
|
return {key: item[key] for key in TEMPLATE_METADATA_KEYS if key in item}
|
||||||
|
|
||||||
|
|
||||||
|
def merge_template_metadata(*metadata_values: Any) -> dict[str, Any]:
|
||||||
|
merged: dict[str, Any] = {}
|
||||||
|
for value in metadata_values:
|
||||||
|
metadata = template_metadata(value)
|
||||||
|
if not metadata:
|
||||||
|
continue
|
||||||
|
for key in ("action_family", "action_type", "family", "position_family", "position_key"):
|
||||||
|
if str(metadata.get(key) or "").strip():
|
||||||
|
merged[key] = metadata[key]
|
||||||
|
if metadata.get("position_keys") is not None:
|
||||||
|
merged["position_keys"] = merge_position_keys(
|
||||||
|
template_position_keys(merged),
|
||||||
|
template_position_keys(metadata),
|
||||||
|
)
|
||||||
|
hint_map = formatter_hints(metadata)
|
||||||
|
if hint_map:
|
||||||
|
existing = formatter_hints(merged)
|
||||||
|
for route, hints in hint_map.items():
|
||||||
|
for hint in hints:
|
||||||
|
if hint not in existing.setdefault(route, []):
|
||||||
|
existing[route].append(hint)
|
||||||
|
merged["formatter_hint"] = existing
|
||||||
|
return merged
|
||||||
|
|
||||||
|
|
||||||
|
def inherited_template_metadata(*containers: Any) -> dict[str, Any]:
|
||||||
|
metadata_parts: list[dict[str, Any]] = []
|
||||||
|
for container in containers:
|
||||||
|
if not isinstance(container, dict):
|
||||||
|
continue
|
||||||
|
nested = container.get("item_template_metadata")
|
||||||
|
if isinstance(nested, dict):
|
||||||
|
metadata_parts.append(nested)
|
||||||
|
metadata_parts.append(container)
|
||||||
|
return merge_template_metadata(*metadata_parts)
|
||||||
|
|
||||||
|
|
||||||
def template_position_family(metadata: dict[str, Any]) -> str:
|
def template_position_family(metadata: dict[str, Any]) -> str:
|
||||||
return normalize_hardcore_position_family(
|
return normalize_hardcore_position_family(
|
||||||
metadata.get("position_family") or metadata.get("family"),
|
metadata.get("position_family") or metadata.get("family"),
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ Core helper ownership:
|
|||||||
| `builder_prompt_route.py` | Single-prompt builder orchestration, input normalization, seed-axis setup, built-in/custom row routing, legacy location/composition handling, camera application, and final prompt-row normalization. |
|
| `builder_prompt_route.py` | Single-prompt builder orchestration, input normalization, seed-axis setup, built-in/custom row routing, legacy location/composition handling, camera application, and final prompt-row normalization. |
|
||||||
| `builder_config_route.py` | Config-driven prompt-builder request parsing, category/cast/profile/filter helper-node mapping, and direct `build_prompt` kwarg assembly. |
|
| `builder_config_route.py` | Config-driven prompt-builder request parsing, category/cast/profile/filter helper-node mapping, and direct `build_prompt` kwarg assembly. |
|
||||||
| `category_extensions.py` | JSON `pool_extensions`, legacy pool patching, built-in category choice lists, and category/subcategory UI choices. |
|
| `category_extensions.py` | JSON `pool_extensions`, legacy pool patching, built-in category choice lists, and category/subcategory UI choices. |
|
||||||
| `category_template_metadata.py` | Object-style item-template metadata extraction, action/position family normalization, position-key normalization, key merging, and audit validation errors. |
|
| `category_template_metadata.py` | Object-style and inherited item-template metadata extraction, action/position family normalization, position-key normalization, key merging, formatter-hint merging, and audit validation errors. |
|
||||||
| `row_item.py` | Row item selection, weighted item/pair choice, item-template axis filling, and oral/outercourse axis compatibility filters. |
|
| `row_item.py` | Row item selection, weighted item/pair choice, item-template axis filling, and oral/outercourse axis compatibility filters. |
|
||||||
| `row_category_route.py` | Row category/subcategory/item route resolution behind `CategoryItemRoute`, hardcore position-category filtering, cast-count adjustment, pose-vs-content seed-axis choice, item metadata collection, legacy dict compatibility, and pose-category item sanitizing. |
|
| `row_category_route.py` | Row category/subcategory/item route resolution behind `CategoryItemRoute`, hardcore position-category filtering, cast-count adjustment, pose-vs-content seed-axis choice, item metadata collection, legacy dict compatibility, and pose-category item sanitizing. |
|
||||||
| `row_rendering.py` | Row prompt/caption text-field resolution, template selection, safe formatting, default prompt templates, configured-cast descriptor insertion, and POV directive insertion. |
|
| `row_rendering.py` | Row prompt/caption text-field resolution, template selection, safe formatting, default prompt templates, configured-cast descriptor insertion, and POV directive insertion. |
|
||||||
@@ -266,6 +266,12 @@ Important JSON keys:
|
|||||||
map keyed by `krea`, `sdxl`, or `caption`; aliases such as `krea2` and
|
map keyed by `krea`, `sdxl`, or `caption`; aliases such as `krea2` and
|
||||||
`training_caption` are normalized by `category_template_metadata.py` and
|
`training_caption` are normalized by `category_template_metadata.py` and
|
||||||
consumed only by the matching formatter route plus the shared `all` route.
|
consumed only by the matching formatter route plus the shared `all` route.
|
||||||
|
- `item_template_metadata`: optional default route metadata on a category,
|
||||||
|
subcategory, or item. String templates inherit it, and object templates can
|
||||||
|
override it while formatter hints merge.
|
||||||
|
- For mixed hardcore subcategories, `action_family: default` keeps the explicit
|
||||||
|
position family while allowing `row_route_metadata.py` to infer the semantic
|
||||||
|
action family from the selected action/role text.
|
||||||
- `axes`: values used to fill `item_templates`.
|
- `axes`: values used to fill `item_templates`.
|
||||||
- `scene_pool` / `scene_pools` or direct `scenes`: location road.
|
- `scene_pool` / `scene_pools` or direct `scenes`: location road.
|
||||||
- `expression_pool` / `expression_pools` or direct `expressions`: expression road.
|
- `expression_pool` / `expression_pools` or direct `expressions`: expression road.
|
||||||
@@ -505,8 +511,8 @@ plain prompt text. When debugging, inspect these fields before editing pools.
|
|||||||
| `content_seed_axis` | `row_category_route.select_category_item_route` | Debug | Shows whether the item/action was driven by `content` or `pose`. Critical for hardcore pose categories. |
|
| `content_seed_axis` | `row_category_route.select_category_item_route` | Debug | Shows whether the item/action was driven by `content` or `pose`. Critical for hardcore pose categories. |
|
||||||
| `item` | `row_category_route.select_category_item_route` or Insta override | Krea/SDXL/Naturalizer | Clothing item, category item, or sexual scene/action text. |
|
| `item` | `row_category_route.select_category_item_route` or Insta override | Krea/SDXL/Naturalizer | Clothing item, category item, or sexual scene/action text. |
|
||||||
| `item_axis_values` | `row_category_route.select_category_item_route` | Krea hardcore rewrite, SDXL tags | Filled template axes such as position/action/detail values. |
|
| `item_axis_values` | `row_category_route.select_category_item_route` | Krea hardcore rewrite, SDXL tags | Filled template axes such as position/action/detail values. |
|
||||||
| `item_template_metadata` | `row_category_route.select_category_item_route` | Debug, Krea/SDXL/Naturalizer route metadata | Optional metadata from object-style item templates; currently used to prefer explicit action/position families and keys before inference. |
|
| `item_template_metadata` | `row_category_route.select_category_item_route` | Debug, Krea/SDXL/Naturalizer route metadata | Metadata inherited from category/subcategory/item `item_template_metadata` plus selected object-template metadata; used to prefer explicit action/position families and keys before inference. |
|
||||||
| `formatter_hints` | `row_category_route.select_category_item_route` | Krea/SDXL/Naturalizer route specialization, debug | Normalized route-specific hints from object-style item templates, keyed by `all`, `krea`, `sdxl`, or `caption`; each formatter consumes `all` plus its own route only. |
|
| `formatter_hints` | `row_category_route.select_category_item_route` | Krea/SDXL/Naturalizer route specialization, debug | Normalized route-specific hints inherited from template metadata, keyed by `all`, `krea`, `sdxl`, or `caption`; each formatter consumes `all` plus its own route only. |
|
||||||
| `action_family` | `row_route_metadata.resolve_action_position_route` | Krea hardcore rewrite, SDXL tags, natural captions, debug | Source-aware formatter semantic family such as `foreplay`, `outercourse`, `oral`, `penetration`, `toy_double`, or `climax`. |
|
| `action_family` | `row_route_metadata.resolve_action_position_route` | Krea hardcore rewrite, SDXL tags, natural captions, debug | Source-aware formatter semantic family such as `foreplay`, `outercourse`, `oral`, `penetration`, `toy_double`, or `climax`. |
|
||||||
| `position_family` | `row_route_metadata.resolve_action_position_route` | Debug/filtering | Source/UI hardcore family selected by template metadata or subcategory, such as `manual`, `interaction`, `oral`, `anal`, or `climax`. |
|
| `position_family` | `row_route_metadata.resolve_action_position_route` | Debug/filtering | Source/UI hardcore family selected by template metadata or subcategory, such as `manual`, `interaction`, `oral`, `anal`, or `climax`. |
|
||||||
| `position_key`, `position_keys` | `row_route_metadata.resolve_action_position_route` | Debug/future filters | Concrete position tokens from object-template metadata and inferred axes/role text, such as `kneeling`, `doggy`, `boobjob`, or `open_thighs`. |
|
| `position_key`, `position_keys` | `row_route_metadata.resolve_action_position_route` | Debug/future filters | Concrete position tokens from object-template metadata and inferred axes/role text, such as `kneeling`, `doggy`, `boobjob`, or `open_thighs`. |
|
||||||
|
|||||||
+11
-2
@@ -309,6 +309,7 @@ def compose_item(
|
|||||||
) -> tuple[str, str, dict[str, str], dict[str, Any]]:
|
) -> tuple[str, str, dict[str, str], dict[str, Any]]:
|
||||||
templates = category_policy.template_list(category, subcategory, item, "item_templates")
|
templates = category_policy.template_list(category, subcategory, item, "item_templates")
|
||||||
axes = category_policy.merged_axes(category, subcategory, item)
|
axes = category_policy.merged_axes(category, subcategory, item)
|
||||||
|
inherited_metadata = template_policy.inherited_template_metadata(category, subcategory, item)
|
||||||
if templates and axes:
|
if templates and axes:
|
||||||
template_entry = weighted_choice(rng, category_policy.compatible_entries(templates, women_count, men_count))
|
template_entry = weighted_choice(rng, category_policy.compatible_entries(templates, women_count, men_count))
|
||||||
template = entry_text(template_entry)
|
template = entry_text(template_entry)
|
||||||
@@ -339,5 +340,13 @@ def compose_item(
|
|||||||
axis_values[name] = entry_text(weighted_choice(rng, values))
|
axis_values[name] = entry_text(weighted_choice(rng, values))
|
||||||
item_prompt = _format(template, axis_values).strip()
|
item_prompt = _format(template, axis_values).strip()
|
||||||
name = item_name(item) or subcategory["name"]
|
name = item_name(item) or subcategory["name"]
|
||||||
return item_prompt, name, axis_values, template_policy.template_metadata(template_entry)
|
return (
|
||||||
return item_text(item), item_name(item), {}, template_policy.template_metadata(item)
|
item_prompt,
|
||||||
|
name,
|
||||||
|
axis_values,
|
||||||
|
template_policy.merge_template_metadata(inherited_metadata, template_policy.template_metadata(template_entry)),
|
||||||
|
)
|
||||||
|
return item_text(item), item_name(item), {}, template_policy.merge_template_metadata(
|
||||||
|
inherited_metadata,
|
||||||
|
template_policy.template_metadata(item),
|
||||||
|
)
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ def resolve_action_position_route_result(
|
|||||||
template_policy.template_position_keys(metadata),
|
template_policy.template_position_keys(metadata),
|
||||||
inferred_position_keys,
|
inferred_position_keys,
|
||||||
)
|
)
|
||||||
action_family = template_policy.template_action_family(metadata)
|
explicit_action_family = template_policy.template_action_family(metadata)
|
||||||
|
action_family = "" if explicit_action_family == "default" else explicit_action_family
|
||||||
if not action_family:
|
if not action_family:
|
||||||
action_family = source_hardcore_action_family(
|
action_family = source_hardcore_action_family(
|
||||||
position_family,
|
position_family,
|
||||||
|
|||||||
@@ -250,6 +250,19 @@ def _template_axis_errors(path: str, node: dict[str, Any]) -> list[tuple[str, st
|
|||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
def _container_template_metadata_errors(path: str, node: dict[str, Any]) -> list[tuple[str, str]]:
|
||||||
|
if "item_template_metadata" not in node:
|
||||||
|
return []
|
||||||
|
metadata = node.get("item_template_metadata")
|
||||||
|
if not isinstance(metadata, dict):
|
||||||
|
return [(f"{path}.item_template_metadata", "item_template_metadata must be an object")]
|
||||||
|
normalized = template_metadata_policy.template_metadata(metadata)
|
||||||
|
return [
|
||||||
|
(f"{path}.item_template_metadata", issue)
|
||||||
|
for issue in template_metadata_policy.template_metadata_errors(normalized)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def _walk_json_references(
|
def _walk_json_references(
|
||||||
value: Any,
|
value: Any,
|
||||||
*,
|
*,
|
||||||
@@ -261,6 +274,10 @@ def _walk_json_references(
|
|||||||
) -> None:
|
) -> None:
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
errors.extend((file_name, item_path, issue) for item_path, issue in _template_axis_errors(path, value))
|
errors.extend((file_name, item_path, issue) for item_path, issue in _template_axis_errors(path, value))
|
||||||
|
errors.extend(
|
||||||
|
(file_name, item_path, issue)
|
||||||
|
for item_path, issue in _container_template_metadata_errors(path, value)
|
||||||
|
)
|
||||||
for key, child in value.items():
|
for key, child in value.items():
|
||||||
if at_root and key in POOL_DEFINITION_KEYS and isinstance(child, dict):
|
if at_root and key in POOL_DEFINITION_KEYS and isinstance(child, dict):
|
||||||
for pool_name, pool_values in child.items():
|
for pool_name, pool_values in child.items():
|
||||||
@@ -318,6 +335,30 @@ def _json_reference_errors(paths: list[Path]) -> list[tuple[str, str, str]]:
|
|||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
def _hardcore_template_metadata_errors(paths: list[Path]) -> list[tuple[str, str, str]]:
|
||||||
|
errors: list[tuple[str, str, str]] = []
|
||||||
|
for path in paths:
|
||||||
|
data = _load_category_json(path)
|
||||||
|
for category in data.get("categories") or []:
|
||||||
|
if str(category.get("slug") or "") != "hardcore_sexual_poses":
|
||||||
|
continue
|
||||||
|
for subcategory in category.get("subcategories") or []:
|
||||||
|
templates = subcategory.get("item_templates")
|
||||||
|
if not isinstance(templates, list) or not templates:
|
||||||
|
continue
|
||||||
|
sub_path = f"categories.{category.get('slug')}.subcategories.{subcategory.get('slug')}"
|
||||||
|
metadata = subcategory.get("item_template_metadata")
|
||||||
|
if not isinstance(metadata, dict):
|
||||||
|
errors.append((path.name, sub_path, "missing item_template_metadata default block"))
|
||||||
|
continue
|
||||||
|
normalized = template_metadata_policy.template_metadata(metadata)
|
||||||
|
if not template_metadata_policy.template_action_family(normalized):
|
||||||
|
errors.append((path.name, f"{sub_path}.item_template_metadata", "missing normalized action_family"))
|
||||||
|
if not template_metadata_policy.template_position_family(normalized):
|
||||||
|
errors.append((path.name, f"{sub_path}.item_template_metadata", "missing normalized position_family"))
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
def _smoke_case_names(path: Path) -> set[str]:
|
def _smoke_case_names(path: Path) -> set[str]:
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
return set()
|
return set()
|
||||||
@@ -445,6 +486,13 @@ def main() -> int:
|
|||||||
return 1
|
return 1
|
||||||
print("OK: all JSON pool references and item template axes resolve.")
|
print("OK: all JSON pool references and item template axes resolve.")
|
||||||
|
|
||||||
|
print("\n# Hardcore Template Metadata Validation")
|
||||||
|
hardcore_metadata_errors = _hardcore_template_metadata_errors(category_paths)
|
||||||
|
if hardcore_metadata_errors:
|
||||||
|
print_table(("File", "Path", "Issue"), hardcore_metadata_errors)
|
||||||
|
return 1
|
||||||
|
print("OK: hardcore template subcategories define explicit route metadata defaults.")
|
||||||
|
|
||||||
print("\n# Routing Documentation Validation")
|
print("\n# Routing Documentation Validation")
|
||||||
routing_doc_errors = _routing_doc_errors()
|
routing_doc_errors = _routing_doc_errors()
|
||||||
if routing_doc_errors:
|
if routing_doc_errors:
|
||||||
|
|||||||
@@ -3936,6 +3936,19 @@ def smoke_hardcore_position_config_policy() -> None:
|
|||||||
"generic contact",
|
"generic contact",
|
||||||
)
|
)
|
||||||
_expect(source_action_family == "outercourse", "Source action-family fallback should accept hyphenated source aliases")
|
_expect(source_action_family == "outercourse", "Source action-family fallback should accept hyphenated source aliases")
|
||||||
|
default_action_route = row_route_metadata.resolve_action_position_route(
|
||||||
|
is_pose_category=True,
|
||||||
|
subcategory={"slug": "anal_double_penetration"},
|
||||||
|
hardcore_position_config=None,
|
||||||
|
item_template_metadata={"action_family": "default", "position_family": "anal"},
|
||||||
|
item_text="toy-assisted double penetration with front-and-back contact",
|
||||||
|
source_role_graph="one partner between two bodies",
|
||||||
|
source_composition="",
|
||||||
|
pose="",
|
||||||
|
item_axis_values={"double_act": "toy-assisted double penetration"},
|
||||||
|
)
|
||||||
|
_expect(default_action_route.get("position_family") == "anal", "Default-action metadata should preserve position family")
|
||||||
|
_expect(default_action_route.get("action_family") == "toy_double", "Default-action metadata should still allow action inference")
|
||||||
item_text, item_name, axis_values, template_metadata = pb._compose_item(
|
item_text, item_name, axis_values, template_metadata = pb._compose_item(
|
||||||
random.Random(42),
|
random.Random(42),
|
||||||
{},
|
{},
|
||||||
@@ -3974,6 +3987,70 @@ def smoke_hardcore_position_config_policy() -> None:
|
|||||||
_expect(formatter_hints.get("krea") == ["keep mouth contact readable"], "Template metadata route lost Krea formatter hint")
|
_expect(formatter_hints.get("krea") == ["keep mouth contact readable"], "Template metadata route lost Krea formatter hint")
|
||||||
_expect(formatter_hints.get("sdxl") == ["oral contact", "kneeling oral"], "Template metadata route lost SDXL formatter hints")
|
_expect(formatter_hints.get("sdxl") == ["oral contact", "kneeling oral"], "Template metadata route lost SDXL formatter hints")
|
||||||
_expect(formatter_hints.get("caption") == ["oral contact caption detail"], "Template metadata route lost caption formatter hint")
|
_expect(formatter_hints.get("caption") == ["oral contact caption detail"], "Template metadata route lost caption formatter hint")
|
||||||
|
inherited_text, _inherited_name, inherited_axis_values, inherited_metadata = pb._compose_item(
|
||||||
|
random.Random(42),
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
"name": "Inherited metadata route",
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "manual",
|
||||||
|
"position_keys": ["kneeling"],
|
||||||
|
"formatter_hint": {"caption": "inherited caption cue"},
|
||||||
|
},
|
||||||
|
"item_templates": ["{act} in {position}"],
|
||||||
|
"item_axes": {
|
||||||
|
"act": ["hand stimulation"],
|
||||||
|
"position": ["kneeling manual position"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Inherited metadata route",
|
||||||
|
women_count=1,
|
||||||
|
men_count=1,
|
||||||
|
)
|
||||||
|
_expect(inherited_text == "hand stimulation in kneeling manual position", "Inherited template metadata changed item text")
|
||||||
|
_expect(inherited_axis_values == {"act": "hand stimulation", "position": "kneeling manual position"}, "Inherited template metadata lost axis values")
|
||||||
|
_expect(inherited_metadata.get("action_family") == "foreplay", "String template did not inherit action family")
|
||||||
|
_expect(inherited_metadata.get("position_family") == "manual", "String template did not inherit position family")
|
||||||
|
_expect(pb._template_position_keys(inherited_metadata) == ["kneeling"], "String template did not inherit position keys")
|
||||||
|
_expect(
|
||||||
|
route_metadata.row_formatter_hints({"item_template_metadata": inherited_metadata}, "caption") == ["inherited caption cue"],
|
||||||
|
"String template did not inherit formatter hints",
|
||||||
|
)
|
||||||
|
override_text, _override_name, _override_axis_values, override_metadata = pb._compose_item(
|
||||||
|
random.Random(42),
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
"name": "Override metadata route",
|
||||||
|
"item_template_metadata": {
|
||||||
|
"action_family": "foreplay",
|
||||||
|
"position_family": "manual",
|
||||||
|
"formatter_hint": {"all": "inherited shared cue"},
|
||||||
|
},
|
||||||
|
"item_templates": [
|
||||||
|
{
|
||||||
|
"template": "{act} in {position}",
|
||||||
|
"action_family": "oral",
|
||||||
|
"formatter_hint": {"krea2": "override krea cue"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"item_axes": {
|
||||||
|
"act": ["mouth contact"],
|
||||||
|
"position": ["kneeling oral position"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Override metadata route",
|
||||||
|
women_count=1,
|
||||||
|
men_count=1,
|
||||||
|
)
|
||||||
|
_expect(override_text == "mouth contact in kneeling oral position", "Override template metadata changed item text")
|
||||||
|
_expect(override_metadata.get("action_family") == "oral", "Template object did not override inherited action family")
|
||||||
|
_expect(override_metadata.get("position_family") == "manual", "Template object should keep inherited position family when absent")
|
||||||
|
_expect(
|
||||||
|
route_metadata.row_formatter_hints({"item_template_metadata": override_metadata}, "krea")
|
||||||
|
== ["inherited shared cue", "override krea cue"],
|
||||||
|
"Template metadata did not merge inherited and template formatter hints",
|
||||||
|
)
|
||||||
route_row = {
|
route_row = {
|
||||||
"action_family": "penetrative",
|
"action_family": "penetrative",
|
||||||
"position_family": "Oral",
|
"position_family": "Oral",
|
||||||
|
|||||||
Reference in New Issue
Block a user