Extract Krea POV support helpers
This commit is contained in:
@@ -149,11 +149,13 @@ Already isolated:
|
|||||||
- `krea_action_context.py` owns shared action-family predicates, axis context
|
- `krea_action_context.py` owns shared action-family predicates, axis context
|
||||||
text, climax detection, and detail-density normalization used by action and
|
text, climax detection, and detail-density normalization used by action and
|
||||||
POV formatter routes.
|
POV formatter routes.
|
||||||
|
- `krea_pov.py` owns POV labels, POV label filtering, and POV camera/composition
|
||||||
|
support text.
|
||||||
|
|
||||||
Improve later:
|
Improve later:
|
||||||
|
|
||||||
- split semantic blocks into modules:
|
- split semantic blocks into modules:
|
||||||
`krea_actions.py`, `krea_pov.py`;
|
`krea_actions.py`, `krea_pov_actions.py`;
|
||||||
- add route-level smoke fixtures for representative metadata rows;
|
- add route-level smoke fixtures for representative metadata rows;
|
||||||
- make `_hardcore_action_sentence` dispatch by action family instead of long
|
- make `_hardcore_action_sentence` dispatch by action family instead of long
|
||||||
conditional chains.
|
conditional chains.
|
||||||
|
|||||||
@@ -548,6 +548,7 @@ Key Krea2 ownership:
|
|||||||
- Cast descriptor naturalization: `krea_cast.cast_prose`,
|
- Cast descriptor naturalization: `krea_cast.cast_prose`,
|
||||||
`krea_cast.natural_label_text`.
|
`krea_cast.natural_label_text`.
|
||||||
- Action context and family predicates: `krea_action_context.py`.
|
- Action context and family predicates: `krea_action_context.py`.
|
||||||
|
- POV labels, filtering, and camera/composition support: `krea_pov.py`.
|
||||||
- Hardcore action sentence: `_hardcore_action_sentence`.
|
- Hardcore action sentence: `_hardcore_action_sentence`.
|
||||||
- POV hardcore sentence: `_pov_hardcore_pose_sentence`, `_pov_action_phrase`.
|
- POV hardcore sentence: `_pov_hardcore_pose_sentence`, `_pov_action_phrase`.
|
||||||
- Clothing state cleanup: `krea_clothing.natural_clothing_state`.
|
- Clothing state cleanup: `krea_clothing.natural_clothing_state`.
|
||||||
@@ -744,9 +745,11 @@ Use these traces to narrow a problem in one pass.
|
|||||||
2. Confirm Krea input uses metadata, not plain prompt fallback.
|
2. Confirm Krea input uses metadata, not plain prompt fallback.
|
||||||
3. Inspect `source_role_graph`, `item`, `source_composition`, and
|
3. Inspect `source_role_graph`, `item`, `source_composition`, and
|
||||||
`item_axis_values`.
|
`item_axis_values`.
|
||||||
4. Edit `_pov_hardcore_pose_sentence` if the first-person body geometry is
|
4. Inspect `krea_pov.py` if the label omission, camera phrase, or POV
|
||||||
|
composition cleanup is wrong.
|
||||||
|
5. Edit `_pov_hardcore_pose_sentence` if the first-person body geometry is
|
||||||
wrong.
|
wrong.
|
||||||
5. Edit `sexual_poses.json` if the raw action lacks enough body-position anchor
|
6. Edit `sexual_poses.json` if the raw action lacks enough body-position anchor
|
||||||
for any formatter to infer a good POV prompt.
|
for any formatter to infer a good POV prompt.
|
||||||
|
|
||||||
### Camera disappears or becomes too generic
|
### Camera disappears or becomes too generic
|
||||||
|
|||||||
+14
-66
@@ -29,6 +29,13 @@ try:
|
|||||||
prompt_cast_descriptors as _prompt_cast_descriptors,
|
prompt_cast_descriptors as _prompt_cast_descriptors,
|
||||||
)
|
)
|
||||||
from .krea_clothing import natural_clothing_state as _natural_clothing_state
|
from .krea_clothing import natural_clothing_state as _natural_clothing_state
|
||||||
|
from .krea_pov import (
|
||||||
|
filter_pov_labeled_clauses as _filter_pov_labeled_clauses,
|
||||||
|
merge_labels as _merge_labels,
|
||||||
|
pov_camera_phrase as _pov_camera_phrase,
|
||||||
|
pov_composition_text as _pov_composition_text,
|
||||||
|
pov_labels_from_value as _pov_labels_from_value,
|
||||||
|
)
|
||||||
from .prompt_hygiene import sanitize_negative_text, sanitize_prose_text
|
from .prompt_hygiene import sanitize_negative_text, sanitize_prose_text
|
||||||
except ImportError: # Allows local smoke tests with `python -c`.
|
except ImportError: # Allows local smoke tests with `python -c`.
|
||||||
from krea_action_context import (
|
from krea_action_context import (
|
||||||
@@ -55,6 +62,13 @@ except ImportError: # Allows local smoke tests with `python -c`.
|
|||||||
prompt_cast_descriptors as _prompt_cast_descriptors,
|
prompt_cast_descriptors as _prompt_cast_descriptors,
|
||||||
)
|
)
|
||||||
from krea_clothing import natural_clothing_state as _natural_clothing_state
|
from krea_clothing import natural_clothing_state as _natural_clothing_state
|
||||||
|
from krea_pov import (
|
||||||
|
filter_pov_labeled_clauses as _filter_pov_labeled_clauses,
|
||||||
|
merge_labels as _merge_labels,
|
||||||
|
pov_camera_phrase as _pov_camera_phrase,
|
||||||
|
pov_composition_text as _pov_composition_text,
|
||||||
|
pov_labels_from_value as _pov_labels_from_value,
|
||||||
|
)
|
||||||
from prompt_hygiene import sanitize_negative_text, sanitize_prose_text
|
from prompt_hygiene import sanitize_negative_text, sanitize_prose_text
|
||||||
|
|
||||||
|
|
||||||
@@ -230,41 +244,6 @@ def _combine_negative(*parts: str) -> str:
|
|||||||
return ", ".join(cleaned)
|
return ", ".join(cleaned)
|
||||||
|
|
||||||
|
|
||||||
def _pov_labels_from_value(value: Any) -> list[str]:
|
|
||||||
labels: list[str] = []
|
|
||||||
if isinstance(value, list):
|
|
||||||
candidates = value
|
|
||||||
else:
|
|
||||||
candidates = re.split(r"[,;]\s*", _clean(value)) if _clean(value) else []
|
|
||||||
for candidate in candidates:
|
|
||||||
label = _clean(candidate)
|
|
||||||
if re.match(r"^Man [A-Z]$", label) and label not in labels:
|
|
||||||
labels.append(label)
|
|
||||||
return labels
|
|
||||||
|
|
||||||
|
|
||||||
def _merge_labels(*groups: list[str]) -> list[str]:
|
|
||||||
merged: list[str] = []
|
|
||||||
for group in groups:
|
|
||||||
for label in group:
|
|
||||||
if label and label not in merged:
|
|
||||||
merged.append(label)
|
|
||||||
return merged
|
|
||||||
|
|
||||||
|
|
||||||
def _filter_pov_labeled_clauses(text: Any, pov_labels: list[str]) -> str:
|
|
||||||
rendered = _clean(text)
|
|
||||||
if not rendered or not pov_labels:
|
|
||||||
return rendered
|
|
||||||
clauses = [clause.strip() for clause in rendered.split(";") if clause.strip()]
|
|
||||||
filtered = [
|
|
||||||
clause
|
|
||||||
for clause in clauses
|
|
||||||
if not any(re.match(rf"^{re.escape(label)}\b", clause) for label in pov_labels)
|
|
||||||
]
|
|
||||||
return "; ".join(filtered)
|
|
||||||
|
|
||||||
|
|
||||||
def _pov_ejaculation_target(context: str) -> str:
|
def _pov_ejaculation_target(context: str) -> str:
|
||||||
if any(token in context for token in ("face", "mouth", "lips", "tongue", "chin")):
|
if any(token in context for token in ("face", "mouth", "lips", "tongue", "chin")):
|
||||||
return "onto her face and chest"
|
return "onto her face and chest"
|
||||||
@@ -562,37 +541,6 @@ def _pov_action_phrase(
|
|||||||
return rendered
|
return rendered
|
||||||
|
|
||||||
|
|
||||||
def _pov_camera_phrase(pov_labels: list[str], softcore: bool = False) -> str:
|
|
||||||
if not pov_labels:
|
|
||||||
return ""
|
|
||||||
if softcore:
|
|
||||||
return (
|
|
||||||
"Camera is the male participant's first-person creator view in one continuous frame, with him implied by perspective or foreground cues"
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
"Camera is the male participant's first-person view in one continuous frame; only his foreground hands or body cues appear"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _pov_composition_text(composition: Any, pov_labels: list[str]) -> str:
|
|
||||||
text = _clean(composition)
|
|
||||||
if not text or not pov_labels:
|
|
||||||
return text
|
|
||||||
text = re.sub(r"\ball participants visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
|
||||||
text = re.sub(r"\ball adult bodies visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
|
||||||
text = re.sub(r"\ball bodies visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
|
||||||
text = re.sub(r"\ball three bodies readable\b", "visible partner bodies readable", text, flags=re.IGNORECASE)
|
|
||||||
text = re.sub(r"\bwide group-sex composition\b", "first-person group-sex POV composition", text, flags=re.IGNORECASE)
|
|
||||||
text = re.sub(
|
|
||||||
r",?\s*adapted for first-person POV with the POV participant kept off-camera\b",
|
|
||||||
"",
|
|
||||||
text,
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
)
|
|
||||||
text = re.sub(r",?\s*with the POV participant kept off-camera\b", "", text, flags=re.IGNORECASE)
|
|
||||||
return _clean(text)
|
|
||||||
|
|
||||||
|
|
||||||
def _sanitize_scene_text_for_cast(text: Any, labels: list[str]) -> str:
|
def _sanitize_scene_text_for_cast(text: Any, labels: list[str]) -> str:
|
||||||
text = _clean(text)
|
text = _clean(text)
|
||||||
if not text:
|
if not text:
|
||||||
|
|||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
def _clean(value: Any) -> str:
|
||||||
|
text = "" if value is None else str(value)
|
||||||
|
text = text.replace("\n", " ")
|
||||||
|
text = re.sub(r"\s+", " ", text).strip()
|
||||||
|
text = re.sub(r"\s+([,.;:])", r"\1", text)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def pov_labels_from_value(value: Any) -> list[str]:
|
||||||
|
labels: list[str] = []
|
||||||
|
if isinstance(value, list):
|
||||||
|
candidates = value
|
||||||
|
else:
|
||||||
|
candidates = re.split(r"[,;]\s*", _clean(value)) if _clean(value) else []
|
||||||
|
for candidate in candidates:
|
||||||
|
label = _clean(candidate)
|
||||||
|
if re.match(r"^Man [A-Z]$", label) and label not in labels:
|
||||||
|
labels.append(label)
|
||||||
|
return labels
|
||||||
|
|
||||||
|
|
||||||
|
def merge_labels(*groups: list[str]) -> list[str]:
|
||||||
|
merged: list[str] = []
|
||||||
|
for group in groups:
|
||||||
|
for label in group:
|
||||||
|
if label and label not in merged:
|
||||||
|
merged.append(label)
|
||||||
|
return merged
|
||||||
|
|
||||||
|
|
||||||
|
def filter_pov_labeled_clauses(text: Any, pov_labels: list[str]) -> str:
|
||||||
|
rendered = _clean(text)
|
||||||
|
if not rendered or not pov_labels:
|
||||||
|
return rendered
|
||||||
|
clauses = [clause.strip() for clause in rendered.split(";") if clause.strip()]
|
||||||
|
filtered = [
|
||||||
|
clause
|
||||||
|
for clause in clauses
|
||||||
|
if not any(re.match(rf"^{re.escape(label)}\b", clause) for label in pov_labels)
|
||||||
|
]
|
||||||
|
return "; ".join(filtered)
|
||||||
|
|
||||||
|
|
||||||
|
def pov_camera_phrase(pov_labels: list[str], softcore: bool = False) -> str:
|
||||||
|
if not pov_labels:
|
||||||
|
return ""
|
||||||
|
if softcore:
|
||||||
|
return (
|
||||||
|
"Camera is the male participant's first-person creator view in one continuous frame, with him implied by perspective or foreground cues"
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
"Camera is the male participant's first-person view in one continuous frame; only his foreground hands or body cues appear"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pov_composition_text(composition: Any, pov_labels: list[str]) -> str:
|
||||||
|
text = _clean(composition)
|
||||||
|
if not text or not pov_labels:
|
||||||
|
return text
|
||||||
|
text = re.sub(r"\ball participants visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
||||||
|
text = re.sub(r"\ball adult bodies visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
||||||
|
text = re.sub(r"\ball bodies visible\b", "visible partners readable", text, flags=re.IGNORECASE)
|
||||||
|
text = re.sub(r"\ball three bodies readable\b", "visible partner bodies readable", text, flags=re.IGNORECASE)
|
||||||
|
text = re.sub(r"\bwide group-sex composition\b", "first-person group-sex POV composition", text, flags=re.IGNORECASE)
|
||||||
|
text = re.sub(
|
||||||
|
r",?\s*adapted for first-person POV with the POV participant kept off-camera\b",
|
||||||
|
"",
|
||||||
|
text,
|
||||||
|
flags=re.IGNORECASE,
|
||||||
|
)
|
||||||
|
text = re.sub(r",?\s*with the POV participant kept off-camera\b", "", text, flags=re.IGNORECASE)
|
||||||
|
return _clean(text)
|
||||||
Reference in New Issue
Block a user