Cover Krea action POV smoke routes

This commit is contained in:
2026-06-26 15:17:18 +02:00
parent b82cf3fbbf
commit a4a8a7a28e
6 changed files with 91 additions and 8 deletions
+13 -7
View File
@@ -57,7 +57,9 @@ It should only handle route-agnostic cleanup:
It must not make semantic decisions such as sexual action positioning, POV It must not make semantic decisions such as sexual action positioning, POV
geometry, clothing state, or model-specific tag weighting. Those stay in the geometry, clothing state, or model-specific tag weighting. Those stay in the
route-specific owner. route-specific owner. It also preserves ordinary words such as `composition`
inside normal sentences; empty field-label cleanup is limited to standalone
labels.
Current integration points: Current integration points:
@@ -93,6 +95,8 @@ Already isolated:
- camera-scene prose and coworking composition adaptation live in - camera-scene prose and coworking composition adaptation live in
`scene_camera_adapters.py`; `prompt_builder.py` still owns camera config `scene_camera_adapters.py`; `prompt_builder.py` still owns camera config
parsing and row mutation. parsing and row mutation.
- shared hardcore environment-anchor cleanup normalizes malformed pool joins
such as `on against a wall` before metadata reaches formatter routes.
### Pair / Adapter Layer ### Pair / Adapter Layer
@@ -248,10 +252,12 @@ Near-term:
formatting across built-in rows, hardcore rows, same-cast pairs, and POV formatting across built-in rows, hardcore rows, same-cast pairs, and POV
pairs. pairs.
- Cover camera-scene preservation through `tools/prompt_smoke.py` for single - Cover camera-scene preservation through `tools/prompt_smoke.py` for single
rows, split soft/hard pair cameras, and POV camera-scene routing. Expand it rows, split soft/hard pair cameras, and POV camera-scene routing.
- Cover config-node routing through `tools/prompt_smoke.py` for category, cast, - Cover config-node routing through `tools/prompt_smoke.py` for category, cast,
generation profile, seed lock, camera, location theme, and composition config. generation profile, seed lock, camera, location theme, and composition config.
Expand it next for close foreplay and POV penetration. - Cover close foreplay and POV penetration Krea routes so raw labels, invalid
surface grammar, normal third-person camera text, and composition punctuation
drift are caught.
Medium-term: Medium-term:
@@ -310,8 +316,8 @@ Medium-term:
## Recommended Next Passes ## Recommended Next Passes
1. Expand `tools/prompt_smoke.py` with close foreplay and POV penetration 1. Split Krea action/POV/clothing helpers into separate modules.
fixtures. 2. Split `__init__.py` node classes by family after behavior is covered by smoke
2. Split Krea action/POV/clothing helpers into separate modules.
3. Split `__init__.py` node classes by family after behavior is covered by smoke
checks. checks.
3. Add metadata fields such as `action_family` / `position_family` to reduce
keyword guessing in hardcore filters and formatter dispatch.
+5
View File
@@ -687,6 +687,11 @@ pair metadata through the core Python APIs, then verifies:
collapsing to the same camera phrase; collapsing to the same camera phrase;
- POV camera-scene directives suppress normal third-person camera text while - POV camera-scene directives suppress normal third-person camera text while
preserving first-person spatial layout; preserving first-person spatial layout;
- Krea close-interaction routes keep rewritten action wording, avoid raw
builder labels, and catch invalid surface joins such as `on against a wall`;
- Krea POV penetration routes keep first-person position anchors, suppress
normal camera text, and preserve composition punctuation before the style
suffix;
- expression-disabled rows do not fall back to generated expression text. - expression-disabled rows do not fall back to generated expression text.
## Editing Cheatsheet ## Editing Cheatsheet
+1
View File
@@ -47,6 +47,7 @@ def _clean(value: Any) -> str:
HARDCORE_ENVIRONMENT_ANCHOR_REPLACEMENTS = ( HARDCORE_ENVIRONMENT_ANCHOR_REPLACEMENTS = (
(r"\bon against a wall\b", "against a wall"),
(r"\bstacked bodies on the bed\b", "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"\bstacked bodies with close body alignment\b", "close body alignment"),
(r"\boverhead tangled-body anal frame\b", "overhead rear-entry anal frame"), (r"\boverhead tangled-body anal frame\b", "overhead rear-entry anal frame"),
+1
View File
@@ -966,6 +966,7 @@ def _heuristic_cast_compatible(text: str, women_count: int, men_count: int) -> b
HARDCORE_ENVIRONMENT_ANCHOR_REPLACEMENTS = ( HARDCORE_ENVIRONMENT_ANCHOR_REPLACEMENTS = (
(r"\bon against a wall\b", "against a wall"),
(r"\bstacked bodies on the bed\b", "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"\bstacked bodies with close body alignment\b", "close body alignment"),
(r"\boverhead tangled-body anal frame\b", "overhead rear-entry anal frame"), (r"\boverhead tangled-body anal frame\b", "overhead rear-entry anal frame"),
+1 -1
View File
@@ -51,7 +51,7 @@ def _strip_empty_fields(text: str) -> str:
labels = "|".join(re.escape(label) for label in EMPTY_FIELD_LABELS) labels = "|".join(re.escape(label) for label in EMPTY_FIELD_LABELS)
text = re.sub(rf"\b(?:{labels})\s*:\s*[.,;]", "", text, flags=re.IGNORECASE) text = re.sub(rf"\b(?:{labels})\s*:\s*[.,;]", "", text, flags=re.IGNORECASE)
text = re.sub(rf"\b(?:{labels}):\s*(?=\.|,|;|$)", "", text, flags=re.IGNORECASE) text = re.sub(rf"\b(?:{labels}):\s*(?=\.|,|;|$)", "", text, flags=re.IGNORECASE)
text = re.sub(rf"\b(?:{labels})\.(?=\s|$)", "", text, flags=re.IGNORECASE) text = re.sub(rf"(^|(?<=[.!?])\s+)(?:{labels})\.(?=\s|$)", r"\1", text, flags=re.IGNORECASE)
text = re.sub(rf"\b(?:{labels}):\s*(?:none|null|n/a)\b[.,;]?", "", text, flags=re.IGNORECASE) text = re.sub(rf"\b(?:{labels}):\s*(?:none|null|n/a)\b[.,;]?", "", text, flags=re.IGNORECASE)
return clean_spacing(text) return clean_spacing(text)
+70
View File
@@ -387,6 +387,32 @@ def smoke_hardcore_category_routes() -> None:
_expect_formatter_outputs(row, name, target="single") _expect_formatter_outputs(row, name, target="single")
def smoke_krea_close_foreplay_route() -> None:
row = _prompt_row(
name="krea_close_foreplay_route",
category="Hardcore sexual poses",
subcategory="Foreplay and teasing",
seed=3401,
character_cast=_character_cast(),
women_count=1,
men_count=1,
hardcore_position_config=_action_filter("foreplay_only"),
)
_expect_custom_row(row, "krea_close_foreplay_route")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
prompt = _expect_text("krea_close_foreplay_route.krea_prompt", krea.get("krea_prompt"), 40)
lower = prompt.lower()
_expect("metadata" in krea.get("method", ""), "close foreplay route did not use metadata")
_expect("role graph:" not in lower, "close foreplay leaked raw role label")
_expect("foreplay action:" not in lower, "close foreplay leaked raw item label")
_expect("on against" not in lower, "close foreplay kept invalid surface grammar")
_expect(
any(term in lower for term in ("clothing", "hands", "kiss", "bodies press", "body contact")),
"close foreplay lost close-contact action wording",
)
_expect_formatter_outputs(row, "krea_close_foreplay_route", target="single")
def _insta_options(**overrides: Any) -> str: def _insta_options(**overrides: Any) -> str:
options = pb.build_insta_of_options_json( options = pb.build_insta_of_options_json(
softcore_cast="same_as_hardcore", softcore_cast="same_as_hardcore",
@@ -553,6 +579,48 @@ def smoke_pov_camera_scene() -> None:
_expect("Camera:" not in prompt, "Krea POV prompt should not emit normal third-person camera directive") _expect("Camera:" not in prompt, "Krea POV prompt should not emit normal third-person camera directive")
def smoke_krea_pov_penetration_route() -> None:
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3411,
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=_action_filter("penetration_only"),
location_config=_coworking_location_config(),
hardcore_camera_config=_orbit_camera(
horizontal_angle=45,
vertical_angle=0,
zoom=5.5,
subject_focus="action",
),
)
_expect_pair(pair, "krea_pov_penetration_route")
hard_row = pair.get("hardcore_row") or {}
_expect("Man A" in (hard_row.get("pov_character_labels") or []), "POV penetration hard row lost Man A POV label")
_expect(not hard_row.get("camera_directive"), "POV penetration should suppress normal camera directive")
krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
prompt = _expect_text("krea_pov_penetration_route.krea_prompt", krea.get("krea_prompt"), 60)
lower = prompt.lower()
_expect("metadata" in krea.get("method", ""), "POV penetration route did not use metadata")
_expect("viewer" in lower and "first-person" in lower, "POV penetration lost first-person wording")
_expect("penetrates" in lower or "penetration" in lower, "POV penetration lost penetration action wording")
_expect("woman" in lower and "thigh" in lower, "POV penetration lost body-position anchors")
_expect("camera:" not in prompt, "POV penetration emitted normal third-person camera directive")
_expect("role graph:" not in lower and "sexual scene:" not in lower, "POV penetration leaked raw prompt labels")
_expect("composition. explicit" in lower, "POV penetration composition sentence should keep punctuation before style suffix")
def smoke_no_expression_fallback() -> None: def smoke_no_expression_fallback() -> None:
cast = pb.build_character_slot_json( cast = pb.build_character_slot_json(
subject_type="woman", subject_type="woman",
@@ -584,10 +652,12 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
("camera_scene_single", smoke_camera_scene_single), ("camera_scene_single", smoke_camera_scene_single),
("config_route_location_theme", smoke_config_route_location_theme), ("config_route_location_theme", smoke_config_route_location_theme),
("hardcore_category_routes", smoke_hardcore_category_routes), ("hardcore_category_routes", smoke_hardcore_category_routes),
("krea_close_foreplay_route", smoke_krea_close_foreplay_route),
("insta_pair_same_cast", smoke_insta_pair), ("insta_pair_same_cast", smoke_insta_pair),
("insta_pair_pov_man", smoke_insta_pair_pov), ("insta_pair_pov_man", smoke_insta_pair_pov),
("insta_pair_camera_split", smoke_insta_pair_camera_split), ("insta_pair_camera_split", smoke_insta_pair_camera_split),
("pov_camera_scene", smoke_pov_camera_scene), ("pov_camera_scene", smoke_pov_camera_scene),
("krea_pov_penetration_route", smoke_krea_pov_penetration_route),
("expression_disabled", smoke_no_expression_fallback), ("expression_disabled", smoke_no_expression_fallback),
] ]