Extract subject context policy
This commit is contained in:
@@ -134,6 +134,9 @@ Already isolated:
|
|||||||
couple count normalization live in `cast_context.py`; `prompt_builder.py`
|
couple count normalization live in `cast_context.py`; `prompt_builder.py`
|
||||||
keeps delegate wrappers where existing generation paths still call the old
|
keeps delegate wrappers where existing generation paths still call the old
|
||||||
helper names.
|
helper names.
|
||||||
|
- row subject-context routing for single, couple, configured-cast, group, and
|
||||||
|
layout subjects lives in `subject_context.py`; it combines appearance policy,
|
||||||
|
cast metadata, and generator subject pools behind one row-facing entry point.
|
||||||
- ethnicity/filter choices, advanced filter JSON, ethnicity-list JSON, filter
|
- ethnicity/filter choices, advanced filter JSON, ethnicity-list JSON, filter
|
||||||
parsing, and ethnicity normalization live in `filter_config.py`; character
|
parsing, and ethnicity normalization live in `filter_config.py`; character
|
||||||
routes and builder filters use `prompt_builder.py` delegate wrappers.
|
routes and builder filters use `prompt_builder.py` delegate wrappers.
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ Core helper ownership:
|
|||||||
| `filter_config.py` | Ethnicity/filter choices, advanced filter JSON, ethnicity-list JSON, filter parsing, and ethnicity normalization used by builder and character routes. |
|
| `filter_config.py` | Ethnicity/filter choices, advanced filter JSON, ethnicity-list JSON, filter parsing, and ethnicity normalization used by builder and character routes. |
|
||||||
| `generation_profile_config.py` | Generation profile presets, profile option overrides, trigger policy, expression/pose/clothing config normalization, and profile config parsing. |
|
| `generation_profile_config.py` | Generation profile presets, profile option overrides, trigger policy, expression/pose/clothing config normalization, and profile config parsing. |
|
||||||
| `seed_config.py` | Seed axis salts/aliases, seed mode choices, global/axis lock JSON builders, seed config parsing, row seed math, and deterministic axis RNG construction. |
|
| `seed_config.py` | Seed axis salts/aliases, seed mode choices, global/axis lock JSON builders, seed config parsing, row seed math, and deterministic axis RNG construction. |
|
||||||
|
| `subject_context.py` | Row subject-context routing for single, couple, configured-cast, group, and layout subjects, combining appearance policy, cast metadata, and generator subject pools. |
|
||||||
| `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. |
|
||||||
@@ -391,9 +392,9 @@ Important behavior:
|
|||||||
|
|
||||||
Edit targets:
|
Edit targets:
|
||||||
|
|
||||||
- Character slot JSON/parsing/summary: `character_slot.py`; generation-time
|
- Subject routing: `subject_context.py`; character slot JSON/parsing/summary:
|
||||||
appearance field resolution: `character_appearance.py`; character-slot label
|
`character_slot.py`; generation-time appearance field resolution:
|
||||||
assignment:
|
`character_appearance.py`; character-slot label assignment:
|
||||||
`cast_context.character_slot_label_map`; pair cast descriptor entry assembly:
|
`cast_context.character_slot_label_map`; pair cast descriptor entry assembly:
|
||||||
`pair_cast.cast_descriptor_entries`.
|
`pair_cast.cast_descriptor_entries`.
|
||||||
- Profile save/load: `SxCPCharacterProfileSave`,
|
- Profile save/load: `SxCPCharacterProfileSave`,
|
||||||
|
|||||||
+12
-53
@@ -47,6 +47,7 @@ try:
|
|||||||
from . import row_camera as row_camera_policy
|
from . import row_camera as row_camera_policy
|
||||||
from . import row_location as row_location_policy
|
from . import row_location as row_location_policy
|
||||||
from . import seed_config as seed_policy
|
from . import seed_config as seed_policy
|
||||||
|
from . import subject_context as subject_context_policy
|
||||||
from .hardcore_text_cleanup import (
|
from .hardcore_text_cleanup import (
|
||||||
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
||||||
sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors,
|
sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors,
|
||||||
@@ -93,6 +94,7 @@ except ImportError: # Allows local smoke tests with `python -c`.
|
|||||||
import row_camera as row_camera_policy
|
import row_camera as row_camera_policy
|
||||||
import row_location as row_location_policy
|
import row_location as row_location_policy
|
||||||
import seed_config as seed_policy
|
import seed_config as seed_policy
|
||||||
|
import subject_context as subject_context_policy
|
||||||
from hardcore_text_cleanup import (
|
from hardcore_text_cleanup import (
|
||||||
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values,
|
||||||
sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors,
|
sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors,
|
||||||
@@ -2498,59 +2500,16 @@ def _subject_context(
|
|||||||
women_count: int = 1,
|
women_count: int = 1,
|
||||||
men_count: int = 1,
|
men_count: int = 1,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
if subject_type in ("woman", "man", "single_any"):
|
return subject_context_policy.subject_context(
|
||||||
return _appearance_for_subject(rng, subject_type, ethnicity, figure, no_plus_women, no_black)
|
rng,
|
||||||
|
subject_type,
|
||||||
if subject_type == "configured_cast":
|
ethnicity,
|
||||||
return _configured_cast_context(women_count, men_count)
|
figure,
|
||||||
|
no_plus_women,
|
||||||
if subject_type == "couple":
|
no_black,
|
||||||
primary_subject, subject_phrase, pose, effective_women_count, effective_men_count = _couple_type_from_counts(
|
women_count,
|
||||||
rng,
|
men_count,
|
||||||
women_count,
|
)
|
||||||
men_count,
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
"subject_type": "couple",
|
|
||||||
"subject": primary_subject,
|
|
||||||
"subject_phrase": subject_phrase,
|
|
||||||
"age": g.choose(rng, g.COUPLE_AGES),
|
|
||||||
"body": g.choose(rng, ["slim and average", "curvy and broad", "stocky and curvy", "average and athletic"]),
|
|
||||||
"skin": "",
|
|
||||||
"hair": "",
|
|
||||||
"eyes": "",
|
|
||||||
"body_phrase": "",
|
|
||||||
"fallback_pose": pose,
|
|
||||||
"women_count": str(effective_women_count),
|
|
||||||
"men_count": str(effective_men_count),
|
|
||||||
"person_count": "2",
|
|
||||||
}
|
|
||||||
|
|
||||||
if subject_type == "group":
|
|
||||||
eth = "Asian " if ethnicity == "asian" else ""
|
|
||||||
return {
|
|
||||||
"subject_type": "group",
|
|
||||||
"subject": f"mixed {eth}adult group",
|
|
||||||
"subject_phrase": f"A mixed {eth}adult group of women and men",
|
|
||||||
"age": g.choose(rng, g.GROUP_AGES),
|
|
||||||
"body": "diverse",
|
|
||||||
"skin": "",
|
|
||||||
"hair": "",
|
|
||||||
"eyes": "",
|
|
||||||
"body_phrase": "diverse adult body types",
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"subject_type": subject_type,
|
|
||||||
"subject": "layout scene",
|
|
||||||
"subject_phrase": "Adult layout scene",
|
|
||||||
"age": "adult",
|
|
||||||
"body": "varied",
|
|
||||||
"skin": "",
|
|
||||||
"hair": "",
|
|
||||||
"eyes": "",
|
|
||||||
"body_phrase": "varied adult figures",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _scene_pool(
|
def _scene_pool(
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import random
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
try:
|
||||||
|
from . import cast_context as cast_context_policy
|
||||||
|
from . import character_appearance as character_appearance_policy
|
||||||
|
from . import generate_prompt_batches as g
|
||||||
|
except ImportError: # Allows local smoke tests with top-level imports.
|
||||||
|
import cast_context as cast_context_policy
|
||||||
|
import character_appearance as character_appearance_policy
|
||||||
|
import generate_prompt_batches as g
|
||||||
|
|
||||||
|
|
||||||
|
def couple_context(
|
||||||
|
rng: random.Random,
|
||||||
|
women_count: int,
|
||||||
|
men_count: int,
|
||||||
|
) -> dict[str, str]:
|
||||||
|
primary_subject, subject_phrase, pose, effective_women_count, effective_men_count = cast_context_policy.couple_type_from_counts(
|
||||||
|
rng,
|
||||||
|
women_count,
|
||||||
|
men_count,
|
||||||
|
choose=g.choose,
|
||||||
|
couple_types=g.COUPLE_TYPES,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"subject_type": "couple",
|
||||||
|
"subject": primary_subject,
|
||||||
|
"subject_phrase": subject_phrase,
|
||||||
|
"age": g.choose(rng, g.COUPLE_AGES),
|
||||||
|
"body": g.choose(rng, ["slim and average", "curvy and broad", "stocky and curvy", "average and athletic"]),
|
||||||
|
"skin": "",
|
||||||
|
"hair": "",
|
||||||
|
"eyes": "",
|
||||||
|
"body_phrase": "",
|
||||||
|
"fallback_pose": pose,
|
||||||
|
"women_count": str(effective_women_count),
|
||||||
|
"men_count": str(effective_men_count),
|
||||||
|
"person_count": "2",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def group_context(rng: random.Random, ethnicity: str) -> dict[str, str]:
|
||||||
|
eth = "Asian " if ethnicity == "asian" else ""
|
||||||
|
return {
|
||||||
|
"subject_type": "group",
|
||||||
|
"subject": f"mixed {eth}adult group",
|
||||||
|
"subject_phrase": f"A mixed {eth}adult group of women and men",
|
||||||
|
"age": g.choose(rng, g.GROUP_AGES),
|
||||||
|
"body": "diverse",
|
||||||
|
"skin": "",
|
||||||
|
"hair": "",
|
||||||
|
"eyes": "",
|
||||||
|
"body_phrase": "diverse adult body types",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def layout_context(subject_type: str) -> dict[str, str]:
|
||||||
|
return {
|
||||||
|
"subject_type": subject_type,
|
||||||
|
"subject": "layout scene",
|
||||||
|
"subject_phrase": "Adult layout scene",
|
||||||
|
"age": "adult",
|
||||||
|
"body": "varied",
|
||||||
|
"skin": "",
|
||||||
|
"hair": "",
|
||||||
|
"eyes": "",
|
||||||
|
"body_phrase": "varied adult figures",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def subject_context(
|
||||||
|
rng: random.Random,
|
||||||
|
subject_type: str,
|
||||||
|
ethnicity: str,
|
||||||
|
figure: str,
|
||||||
|
no_plus_women: bool,
|
||||||
|
no_black: bool,
|
||||||
|
women_count: int = 1,
|
||||||
|
men_count: int = 1,
|
||||||
|
) -> dict[str, str]:
|
||||||
|
if subject_type in ("woman", "man", "single_any"):
|
||||||
|
return character_appearance_policy.appearance_for_subject(
|
||||||
|
rng,
|
||||||
|
subject_type,
|
||||||
|
ethnicity,
|
||||||
|
figure,
|
||||||
|
no_plus_women,
|
||||||
|
no_black,
|
||||||
|
)
|
||||||
|
|
||||||
|
if subject_type == "configured_cast":
|
||||||
|
return cast_context_policy.configured_cast_context(women_count, men_count)
|
||||||
|
|
||||||
|
if subject_type == "couple":
|
||||||
|
return couple_context(rng, women_count, men_count)
|
||||||
|
|
||||||
|
if subject_type == "group":
|
||||||
|
return group_context(rng, ethnicity)
|
||||||
|
|
||||||
|
return layout_context(subject_type)
|
||||||
@@ -57,6 +57,7 @@ import sdxl_formatter # noqa: E402
|
|||||||
import sdxl_presets # noqa: E402
|
import sdxl_presets # noqa: E402
|
||||||
import seed_config # noqa: E402
|
import seed_config # noqa: E402
|
||||||
import krea_pov # noqa: E402
|
import krea_pov # noqa: E402
|
||||||
|
import subject_context # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
Trigger = "sxcppnl7"
|
Trigger = "sxcppnl7"
|
||||||
@@ -684,6 +685,23 @@ def smoke_category_cast_config_policy() -> None:
|
|||||||
== ("two women", "two women", "close affectionate couple pose", 2, 0),
|
== ("two women", "two women", "close affectionate couple pose", 2, 0),
|
||||||
"Couple type count override for two women changed",
|
"Couple type count override for two women changed",
|
||||||
)
|
)
|
||||||
|
_expect(
|
||||||
|
pb._subject_context(random.Random(5), "couple", "any", "curvy", False, False, 1, 1)
|
||||||
|
== subject_context.subject_context(random.Random(5), "couple", "any", "curvy", False, False, 1, 1),
|
||||||
|
"Prompt builder subject context should delegate to subject_context",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
subject_context.subject_context(random.Random(1), "configured_cast", "any", "curvy", False, False, 2, 1).get("cast_summary")
|
||||||
|
== "2 women, 1 man, 3 total adults",
|
||||||
|
"Configured cast subject context changed",
|
||||||
|
)
|
||||||
|
group = subject_context.subject_context(random.Random(2), "group", "asian", "curvy", False, False)
|
||||||
|
_expect(group.get("subject") == "mixed Asian adult group", "Group subject ethnicity wording changed")
|
||||||
|
_expect(
|
||||||
|
subject_context.subject_context(random.Random(3), "layout", "any", "curvy", False, False).get("subject")
|
||||||
|
== "layout scene",
|
||||||
|
"Layout subject context fallback changed",
|
||||||
|
)
|
||||||
label_slots = [
|
label_slots = [
|
||||||
{"subject_type": "woman", "label": "auto_chain", "name": "older auto"},
|
{"subject_type": "woman", "label": "auto_chain", "name": "older auto"},
|
||||||
{"subject_type": "woman", "label": "auto_chain", "name": "newer auto"},
|
{"subject_type": "woman", "label": "auto_chain", "name": "newer auto"},
|
||||||
|
|||||||
Reference in New Issue
Block a user