Extract Insta pair cast context
This commit is contained in:
@@ -144,18 +144,18 @@ Owner today: `build_insta_of_pair`.
|
||||
|
||||
Keep here:
|
||||
|
||||
- continuity policy;
|
||||
- softcore cast policy;
|
||||
|
||||
Improve later:
|
||||
|
||||
- split remaining pair cast/descriptor policy out of `build_insta_of_pair`.
|
||||
- pair route sequencing;
|
||||
- top-level continuity option handoff between row, camera, clothing, and output
|
||||
adapters.
|
||||
|
||||
Already isolated:
|
||||
|
||||
- soft/hard row creation lives in `pair_rows.py`, including softcore expression
|
||||
override resolution, Woman A slot context application, soft outfit/pose
|
||||
overrides, POV row fields, and hardcore row creation.
|
||||
- pair-level cast/display context lives in `pair_cast.py`, including shared
|
||||
descriptors, same-cast softcore descriptor text, partner styling, platform and
|
||||
level labels, softcore cast presence text, and hard cast summary text.
|
||||
- pair-level camera routing lives in `pair_camera.py`, including soft/hard
|
||||
camera config selection, same-as-softcore mode, camera-detail override,
|
||||
same-room hard scene continuity, camera-aware composition mutation, POV camera
|
||||
|
||||
@@ -370,11 +370,16 @@ flowchart TD
|
||||
L[location_config] --> P
|
||||
M[composition_config] --> P
|
||||
H[hardcore_position_config] --> P
|
||||
P --> A[soft_row via build_prompt]
|
||||
P --> B[hard_row via build_prompt]
|
||||
A --> X[pair metadata]
|
||||
B --> X
|
||||
X --> K[Krea2/SDXL/Naturalizer]
|
||||
P --> R[pair_rows.py]
|
||||
R --> A[soft_row via build_prompt]
|
||||
R --> B[hard_row via build_prompt]
|
||||
A --> D[pair_cast.py]
|
||||
B --> D
|
||||
D --> X[pair metadata]
|
||||
B --> Y[pair_camera.py + pair_clothing.py]
|
||||
Y --> X
|
||||
X --> Z[pair_output.py]
|
||||
Z --> K[Krea2/SDXL/Naturalizer]
|
||||
```
|
||||
|
||||
Softcore row:
|
||||
@@ -383,8 +388,8 @@ Softcore row:
|
||||
- Outfit comes from character slot `softcore_outfit` if present, otherwise
|
||||
`INSTA_OF_SOFTCORE_OUTFITS`.
|
||||
- Soft pose comes from `INSTA_OF_SOFTCORE_POSES`.
|
||||
- Partner styling comes from `_insta_of_partner_styling` when softcore cast is
|
||||
`same_as_hardcore`.
|
||||
- Partner styling is resolved through `pair_cast.py` using
|
||||
`_insta_of_partner_styling` when softcore cast is `same_as_hardcore`.
|
||||
|
||||
Hardcore row:
|
||||
|
||||
@@ -455,12 +460,12 @@ plain prompt text. When debugging, inspect these fields before editing pools.
|
||||
| --- | --- | --- | --- |
|
||||
| `mode` | `build_insta_of_pair` | Formatters | `Insta/OF` selects pair formatter branches. |
|
||||
| `options` | `SxCP Insta/OF Options` | Formatters/debug | Soft/hard level, cast mode, continuity, camera modes, expression settings. |
|
||||
| `shared_descriptor` | Soft row descriptor | Pair formatters | Primary creator descriptor. |
|
||||
| `shared_cast_descriptors` | Cast descriptor builder | Pair formatters | Full cast descriptor list. |
|
||||
| `shared_descriptor` | `pair_cast.py` | Pair formatters | Primary creator descriptor. |
|
||||
| `shared_cast_descriptors` | `pair_cast.py` | Pair formatters | Full cast descriptor list. |
|
||||
| `softcore_row`, `hardcore_row` | Pair route | Pair formatters | Full normal metadata rows for each side. |
|
||||
| `softcore_prompt`, `hardcore_prompt` | `pair_output.py` | Direct output/fallback | Raw pair prompts before formatter rewrite. |
|
||||
| `softcore_negative_prompt`, `hardcore_negative_prompt` | `pair_output.py` | Formatter negatives | Separate negatives for each side. |
|
||||
| `softcore_partner_styling` | `_insta_of_partner_styling` | Krea/SDXL pair branch | Partner softcore clothing and pose when same-cast softcore is enabled. |
|
||||
| `softcore_partner_styling` | `pair_cast.py` | Krea/SDXL pair branch | Partner softcore clothing and pose when same-cast softcore is enabled. |
|
||||
| `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. |
|
||||
| `hardcore_clothing_state` | Pair clothing continuity | Krea/SDXL pair branch | Final hard clothing/body exposure sentence before Krea cleanup. |
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
DescriptorFromRow = Callable[[dict[str, Any]], str]
|
||||
CastDescriptors = Callable[..., list[str]]
|
||||
PartnerStyling = Callable[..., dict[str, Any]]
|
||||
CastPhrase = Callable[[int, int], str]
|
||||
|
||||
|
||||
def resolve_insta_pair_cast_context(
|
||||
*,
|
||||
soft_row: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
parsed_seed_config: dict[str, int],
|
||||
seed: int,
|
||||
row_number: int,
|
||||
ethnicity: str,
|
||||
figure: str,
|
||||
no_plus_women: bool,
|
||||
no_black: bool,
|
||||
hard_women_count: int,
|
||||
hard_men_count: int,
|
||||
character_slots: list[dict[str, Any]],
|
||||
character_slot_map: dict[str, dict[str, Any]],
|
||||
pov_character_labels: list[str],
|
||||
platform_styles: dict[str, str],
|
||||
soft_levels: dict[str, str],
|
||||
hardcore_levels: dict[str, str],
|
||||
descriptor_from_row: DescriptorFromRow,
|
||||
build_cast_descriptors: CastDescriptors,
|
||||
prompt_cast_descriptors: Callable[[str], str],
|
||||
partner_styling: PartnerStyling,
|
||||
cast_phrase: CastPhrase,
|
||||
) -> dict[str, Any]:
|
||||
descriptor = descriptor_from_row(soft_row)
|
||||
cast_descriptors = build_cast_descriptors(
|
||||
descriptor,
|
||||
parsed_seed_config,
|
||||
seed,
|
||||
row_number,
|
||||
ethnicity,
|
||||
figure,
|
||||
no_plus_women,
|
||||
no_black,
|
||||
hard_women_count,
|
||||
hard_men_count,
|
||||
character_slots,
|
||||
)
|
||||
cast_descriptor_text = prompt_cast_descriptors("; ".join(cast_descriptors))
|
||||
same_softcore_cast = options["softcore_cast"] == "same_as_hardcore"
|
||||
soft_cast_descriptor_text = cast_descriptor_text if same_softcore_cast else f"Woman A: {descriptor}"
|
||||
soft_partner_styling = partner_styling(
|
||||
parsed_seed_config,
|
||||
seed,
|
||||
row_number,
|
||||
hard_women_count if same_softcore_cast else 1,
|
||||
hard_men_count if same_softcore_cast else 0,
|
||||
pov_character_labels if same_softcore_cast else [],
|
||||
character_slot_map,
|
||||
)
|
||||
if not same_softcore_cast:
|
||||
soft_partner_styling = {"outfits": [], "pose": ""}
|
||||
soft_partner_outfit_text = "; ".join(soft_partner_styling["outfits"])
|
||||
|
||||
soft_cast = (
|
||||
"solo creator setup with Woman A alone"
|
||||
if options["softcore_cast"] == "solo"
|
||||
else f"soft creator-teaser setup with {cast_phrase(hard_women_count, hard_men_count)}"
|
||||
)
|
||||
soft_cast_presence = (
|
||||
(
|
||||
"Frame Woman A from the POV participant's first-person camera in a soft creator-teaser setup; "
|
||||
"keep the POV participant off-camera as the viewpoint and implied by camera perspective or foreground cues. "
|
||||
)
|
||||
if same_softcore_cast and pov_character_labels
|
||||
else (
|
||||
"Place Woman A and the listed partners together in a soft creator-teaser pose. "
|
||||
if same_softcore_cast
|
||||
else "Keep the softcore version focused on Woman A alone. "
|
||||
)
|
||||
)
|
||||
soft_cast_styling_sentence = (
|
||||
f"Partner softcore styling: {soft_partner_outfit_text}. Cast pose: {soft_partner_styling['pose']}. "
|
||||
if same_softcore_cast and soft_partner_outfit_text
|
||||
else ""
|
||||
)
|
||||
hard_cast = cast_phrase(hard_women_count, hard_men_count)
|
||||
soft_descriptor_sentence = (
|
||||
f"Cast descriptors: {soft_cast_descriptor_text}. "
|
||||
if same_softcore_cast
|
||||
else f"Woman A: {descriptor}. "
|
||||
)
|
||||
|
||||
return {
|
||||
"descriptor": descriptor,
|
||||
"cast_descriptors": cast_descriptors,
|
||||
"cast_descriptor_text": cast_descriptor_text,
|
||||
"soft_cast_descriptor_text": soft_cast_descriptor_text,
|
||||
"soft_partner_styling": soft_partner_styling,
|
||||
"soft_partner_outfit_text": soft_partner_outfit_text,
|
||||
"platform_style": platform_styles[options["platform_style"]],
|
||||
"soft_level": soft_levels[options["softcore_level"]],
|
||||
"hard_level": hardcore_levels[options["hardcore_level"]],
|
||||
"soft_cast": soft_cast,
|
||||
"soft_cast_presence": soft_cast_presence,
|
||||
"soft_cast_styling_sentence": soft_cast_styling_sentence,
|
||||
"hard_cast": hard_cast,
|
||||
"soft_descriptor_sentence": soft_descriptor_sentence,
|
||||
}
|
||||
+38
-62
@@ -27,6 +27,7 @@ try:
|
||||
from . import generate_prompt_batches as g
|
||||
from . import pair_clothing
|
||||
from . import pair_camera
|
||||
from . import pair_cast
|
||||
from . import pair_output
|
||||
from . import pair_rows
|
||||
from . import scene_camera_adapters
|
||||
@@ -60,6 +61,7 @@ except ImportError: # Allows local smoke tests with `python -c`.
|
||||
import generate_prompt_batches as g
|
||||
import pair_clothing
|
||||
import pair_camera
|
||||
import pair_cast
|
||||
import pair_output
|
||||
import pair_rows
|
||||
import scene_camera_adapters
|
||||
@@ -7037,41 +7039,38 @@ def build_insta_of_pair(
|
||||
hard_row = row_route["hard_row"]
|
||||
hard_content_rng = row_route["hard_content_rng"]
|
||||
|
||||
descriptor = _insta_of_descriptor(soft_row)
|
||||
cast_descriptors = _insta_of_cast_descriptors(
|
||||
descriptor,
|
||||
parsed_seed_config,
|
||||
seed,
|
||||
row_number,
|
||||
ethnicity,
|
||||
figure,
|
||||
no_plus_women,
|
||||
no_black,
|
||||
hard_women_count,
|
||||
hard_men_count,
|
||||
character_slots,
|
||||
cast_context = pair_cast.resolve_insta_pair_cast_context(
|
||||
soft_row=soft_row,
|
||||
options=options,
|
||||
parsed_seed_config=parsed_seed_config,
|
||||
seed=seed,
|
||||
row_number=row_number,
|
||||
ethnicity=ethnicity,
|
||||
figure=figure,
|
||||
no_plus_women=no_plus_women,
|
||||
no_black=no_black,
|
||||
hard_women_count=hard_women_count,
|
||||
hard_men_count=hard_men_count,
|
||||
character_slots=character_slots,
|
||||
character_slot_map=character_slot_map,
|
||||
pov_character_labels=pov_character_labels,
|
||||
platform_styles=INSTA_OF_PLATFORM_STYLES,
|
||||
soft_levels=INSTA_OF_SOFT_LEVELS,
|
||||
hardcore_levels=INSTA_OF_HARDCORE_LEVELS,
|
||||
descriptor_from_row=_insta_of_descriptor,
|
||||
build_cast_descriptors=_insta_of_cast_descriptors,
|
||||
prompt_cast_descriptors=_insta_of_prompt_cast_descriptors,
|
||||
partner_styling=_insta_of_partner_styling,
|
||||
cast_phrase=_insta_of_cast_phrase,
|
||||
)
|
||||
cast_descriptor_text = _insta_of_prompt_cast_descriptors("; ".join(cast_descriptors))
|
||||
soft_cast_descriptor_text = (
|
||||
cast_descriptor_text
|
||||
if options["softcore_cast"] == "same_as_hardcore"
|
||||
else f"Woman A: {descriptor}"
|
||||
)
|
||||
soft_partner_styling = _insta_of_partner_styling(
|
||||
parsed_seed_config,
|
||||
seed,
|
||||
row_number,
|
||||
hard_women_count if options["softcore_cast"] == "same_as_hardcore" else 1,
|
||||
hard_men_count if options["softcore_cast"] == "same_as_hardcore" else 0,
|
||||
pov_character_labels if options["softcore_cast"] == "same_as_hardcore" else [],
|
||||
character_slot_map,
|
||||
)
|
||||
if options["softcore_cast"] != "same_as_hardcore":
|
||||
soft_partner_styling = {"outfits": [], "pose": ""}
|
||||
soft_partner_outfit_text = "; ".join(soft_partner_styling["outfits"])
|
||||
platform_style = INSTA_OF_PLATFORM_STYLES[options["platform_style"]]
|
||||
soft_level = INSTA_OF_SOFT_LEVELS[options["softcore_level"]]
|
||||
hard_level = INSTA_OF_HARDCORE_LEVELS[options["hardcore_level"]]
|
||||
descriptor = cast_context["descriptor"]
|
||||
cast_descriptors = cast_context["cast_descriptors"]
|
||||
cast_descriptor_text = cast_context["cast_descriptor_text"]
|
||||
soft_partner_styling = cast_context["soft_partner_styling"]
|
||||
soft_partner_outfit_text = cast_context["soft_partner_outfit_text"]
|
||||
platform_style = cast_context["platform_style"]
|
||||
soft_level = cast_context["soft_level"]
|
||||
hard_level = cast_context["hard_level"]
|
||||
camera_route = pair_camera.resolve_insta_pair_camera(
|
||||
soft_row=soft_row,
|
||||
hard_row=hard_row,
|
||||
@@ -7104,29 +7103,10 @@ def build_insta_of_pair(
|
||||
hard_camera_scene_sentence = camera_route["hard_camera_scene_sentence"]
|
||||
soft_camera_sentence = camera_route["soft_camera_sentence"]
|
||||
hard_camera_sentence = camera_route["hard_camera_sentence"]
|
||||
soft_cast = (
|
||||
"solo creator setup with Woman A alone"
|
||||
if options["softcore_cast"] == "solo"
|
||||
else f"soft creator-teaser setup with {_insta_of_cast_phrase(hard_women_count, hard_men_count)}"
|
||||
)
|
||||
soft_cast_presence = (
|
||||
(
|
||||
"Frame Woman A from the POV participant's first-person camera in a soft creator-teaser setup; "
|
||||
"keep the POV participant off-camera as the viewpoint and implied by camera perspective or foreground cues. "
|
||||
)
|
||||
if options["softcore_cast"] == "same_as_hardcore" and pov_character_labels
|
||||
else (
|
||||
"Place Woman A and the listed partners together in a soft creator-teaser pose. "
|
||||
if options["softcore_cast"] == "same_as_hardcore"
|
||||
else "Keep the softcore version focused on Woman A alone. "
|
||||
)
|
||||
)
|
||||
soft_cast_styling_sentence = (
|
||||
f"Partner softcore styling: {soft_partner_outfit_text}. Cast pose: {soft_partner_styling['pose']}. "
|
||||
if options["softcore_cast"] == "same_as_hardcore" and soft_partner_outfit_text
|
||||
else ""
|
||||
)
|
||||
hard_cast = _insta_of_cast_phrase(hard_women_count, hard_men_count)
|
||||
soft_cast = cast_context["soft_cast"]
|
||||
soft_cast_presence = cast_context["soft_cast_presence"]
|
||||
soft_cast_styling_sentence = cast_context["soft_cast_styling_sentence"]
|
||||
hard_cast = cast_context["hard_cast"]
|
||||
character_hardcore_clothing_entries = _character_hardcore_clothing_entries(
|
||||
character_slot_map,
|
||||
hard_women_count,
|
||||
@@ -7160,11 +7140,7 @@ def build_insta_of_pair(
|
||||
"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)
|
||||
soft_descriptor_sentence = (
|
||||
f"Cast descriptors: {soft_cast_descriptor_text}. "
|
||||
if options["softcore_cast"] == "same_as_hardcore"
|
||||
else f"Woman A: {descriptor}. "
|
||||
)
|
||||
soft_descriptor_sentence = cast_context["soft_descriptor_sentence"]
|
||||
|
||||
return pair_output.assemble_insta_pair_metadata(
|
||||
active_trigger=active_trigger,
|
||||
|
||||
@@ -630,6 +630,8 @@ def _expect_pair(pair: dict[str, Any], name: str) -> None:
|
||||
_expect(pair.get("mode") == "Insta/OF", f"{name}.mode should be Insta/OF")
|
||||
_expect_row_base(pair.get("softcore_row") or {}, f"{name}.softcore_row")
|
||||
_expect_custom_row(pair.get("hardcore_row") or {}, f"{name}.hardcore_row")
|
||||
_expect_text(f"{name}.shared_descriptor", pair.get("shared_descriptor"), 12)
|
||||
_expect(pair.get("shared_cast_descriptors"), f"{name}.shared_cast_descriptors should not be empty")
|
||||
_expect_text(f"{name}.softcore_prompt", pair.get("softcore_prompt"), 20)
|
||||
_expect_text(f"{name}.hardcore_prompt", pair.get("hardcore_prompt"), 20)
|
||||
_expect_trigger_once(f"{name}.softcore_prompt", pair.get("softcore_prompt"), Trigger)
|
||||
@@ -662,6 +664,9 @@ def smoke_insta_pair() -> None:
|
||||
clothing_state = _clean_key(pair.get("hardcore_clothing_state"))
|
||||
_expect("body is fully exposed" in clothing_state, "explicit nude pair should keep body exposure state")
|
||||
_expect("teaser outfit detail" not in clothing_state, "explicit nude pair should not repeat softcore outfit detail")
|
||||
partner_styling = pair.get("softcore_partner_styling") or {}
|
||||
_expect(partner_styling.get("outfits"), "same-cast pair should keep partner softcore outfit styling")
|
||||
_expect_text("insta_pair_same_cast.partner_pose", partner_styling.get("pose"), 12)
|
||||
|
||||
|
||||
def smoke_krea_pair_clothing_state() -> None:
|
||||
|
||||
Reference in New Issue
Block a user