Add separate style pool config
This commit is contained in:
@@ -22,6 +22,7 @@ The node is registered as:
|
|||||||
- `prompt_builder / SxCP Cast Control`
|
- `prompt_builder / SxCP Cast Control`
|
||||||
- `prompt_builder / SxCP Cast Bias`
|
- `prompt_builder / SxCP Cast Bias`
|
||||||
- `prompt_builder / SxCP Generation Profile`
|
- `prompt_builder / SxCP Generation Profile`
|
||||||
|
- `prompt_builder / SxCP Style Pool`
|
||||||
- `prompt_builder / SxCP Ethnicity List`
|
- `prompt_builder / SxCP Ethnicity List`
|
||||||
- `prompt_builder / SxCP Hair Length`
|
- `prompt_builder / SxCP Hair Length`
|
||||||
- `prompt_builder / SxCP Hair Color`
|
- `prompt_builder / SxCP Hair Color`
|
||||||
@@ -93,6 +94,11 @@ node. For cleaner workflows, use the split nodes:
|
|||||||
`composition_config`. Themes such as `classical_library`,
|
`composition_config`. Themes such as `classical_library`,
|
||||||
`semi_public_affair`, `hotel_corridor`, `parking_garage`, and
|
`semi_public_affair`, `hotel_corridor`, `parking_garage`, and
|
||||||
`theater_backstage` keep scene and framing compatible.
|
`theater_backstage` keep scene and framing compatible.
|
||||||
|
- `SxCP Style Pool` outputs `style_config` for visual rendering style only.
|
||||||
|
It can force realistic/photo/cinematic/comic output independently from
|
||||||
|
category, action, pose, location, and camera. The previous colored-pencil
|
||||||
|
comic wording is available as the `comic_pinup_colored_pencil` preset instead
|
||||||
|
of being baked into hardcore pose prompts.
|
||||||
- `SxCP Generation Profile` outputs `generation_profile` for common behavior
|
- `SxCP Generation Profile` outputs `generation_profile` for common behavior
|
||||||
presets such as casual-clean, evocative-softcore, hardcore-intense,
|
presets such as casual-clean, evocative-softcore, hardcore-intense,
|
||||||
Krea2-friendly, or Flux-original. Its clothing and pose overrides can be
|
Krea2-friendly, or Flux-original. Its clothing and pose overrides can be
|
||||||
@@ -115,7 +121,7 @@ The practical compact workflow is:
|
|||||||
`Category Preset` + `Cast Control` + `Generation Profile` + optional
|
`Category Preset` + `Cast Control` + `Generation Profile` + optional
|
||||||
`Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control` or
|
`Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control` or
|
||||||
`Camera Orbit Control`, `Location Theme` or `Location Pool` + `Composition Pool`,
|
`Camera Orbit Control`, `Location Theme` or `Location Pool` + `Composition Pool`,
|
||||||
`Woman Slot` / `Man Slot`, and `Character Profile`
|
`Style Pool`, `Woman Slot` / `Man Slot`, and `Character Profile`
|
||||||
into `Prompt Builder From Configs`.
|
into `Prompt Builder From Configs`.
|
||||||
|
|
||||||
## Scene-Chain v2 Nodes
|
## Scene-Chain v2 Nodes
|
||||||
@@ -734,7 +740,7 @@ Example:
|
|||||||
"slug": "casual_clothes",
|
"slug": "casual_clothes",
|
||||||
"subject_type": "woman",
|
"subject_type": "woman",
|
||||||
"item_label": "Clothing",
|
"item_label": "Clothing",
|
||||||
"style": "tasteful adult fashion-editorial coloured-pencil comic illustration",
|
"style": "tasteful adult fashion-editorial scene",
|
||||||
"subcategories": [
|
"subcategories": [
|
||||||
{
|
{
|
||||||
"name": "Streetwear",
|
"name": "Streetwear",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ SXCP_CATEGORY_CONFIG = "SXCP_CATEGORY_CONFIG"
|
|||||||
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
||||||
SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE"
|
SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE"
|
||||||
SXCP_INSTA_OF_OPTIONS = "SXCP_INSTA_OF_OPTIONS"
|
SXCP_INSTA_OF_OPTIONS = "SXCP_INSTA_OF_OPTIONS"
|
||||||
|
SXCP_STYLE_CONFIG = "SXCP_STYLE_CONFIG"
|
||||||
SXCP_HARDCORE_POSITION_CONFIG = "SXCP_HARDCORE_POSITION_CONFIG"
|
SXCP_HARDCORE_POSITION_CONFIG = "SXCP_HARDCORE_POSITION_CONFIG"
|
||||||
SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
|
SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
|
||||||
SXCP_CHARACTER_SLOT = "SXCP_CHARACTER_SLOT"
|
SXCP_CHARACTER_SLOT = "SXCP_CHARACTER_SLOT"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class PromptFromConfigsRequest:
|
|||||||
hardcore_position_config: str | dict[str, Any] | None = ""
|
hardcore_position_config: str | dict[str, Any] | None = ""
|
||||||
location_config: str | dict[str, Any] | None = ""
|
location_config: str | dict[str, Any] | None = ""
|
||||||
composition_config: str | dict[str, Any] | None = ""
|
composition_config: str | dict[str, Any] | None = ""
|
||||||
|
style_config: str | dict[str, Any] | None = ""
|
||||||
extra_positive: str = ""
|
extra_positive: str = ""
|
||||||
extra_negative: str = ""
|
extra_negative: str = ""
|
||||||
|
|
||||||
@@ -82,6 +83,7 @@ def build_prompt_from_configs_result(
|
|||||||
"hardcore_position_config": request.hardcore_position_config or "",
|
"hardcore_position_config": request.hardcore_position_config or "",
|
||||||
"location_config": request.location_config or "",
|
"location_config": request.location_config or "",
|
||||||
"composition_config": request.composition_config or "",
|
"composition_config": request.composition_config or "",
|
||||||
|
"style_config": request.style_config or "",
|
||||||
}
|
}
|
||||||
return PromptFromConfigsRoute(
|
return PromptFromConfigsRoute(
|
||||||
row=deps.build_prompt(**build_kwargs),
|
row=deps.build_prompt(**build_kwargs),
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class PromptBuildRequest:
|
|||||||
hardcore_position_config: str | dict[str, Any] | None = None
|
hardcore_position_config: str | dict[str, Any] | None = None
|
||||||
location_config: str | dict[str, Any] | None = None
|
location_config: str | dict[str, Any] | None = None
|
||||||
composition_config: str | dict[str, Any] | None = None
|
composition_config: str | dict[str, Any] | None = None
|
||||||
|
style_config: str | dict[str, Any] | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -226,6 +227,7 @@ def build_prompt_result(request: PromptBuildRequest, deps: PromptBuildDependenci
|
|||||||
request.hardcore_position_config,
|
request.hardcore_position_config,
|
||||||
parsed_location_config,
|
parsed_location_config,
|
||||||
parsed_composition_config,
|
parsed_composition_config,
|
||||||
|
request.style_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
if row.get("source") == "built_in_generator":
|
if row.get("source") == "built_in_generator":
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"subject_type": "woman",
|
"subject_type": "woman",
|
||||||
"item_label": "Clothing",
|
"item_label": "Clothing",
|
||||||
"style": "tasteful adult fashion-editorial coloured-pencil comic illustration with casual everyday styling",
|
"style": "tasteful adult fashion-editorial scene with casual everyday styling",
|
||||||
"positive_suffix": "Use crisp clean comic linework, soft fabric texture, detailed hatching, warm natural light, and tactile textured paper.",
|
"positive_suffix": "Use readable full outfits, clear fabric texture, natural light, coherent anatomy, and polished styling detail.",
|
||||||
"expression_pools": ["casual_observational_expressions"],
|
"expression_pools": ["casual_observational_expressions"],
|
||||||
"composition_pools": ["casual_fashion_compositions"],
|
"composition_pools": ["casual_fashion_compositions"],
|
||||||
"subcategories": [
|
"subcategories": [
|
||||||
@@ -833,8 +833,8 @@
|
|||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"subject_type": "man",
|
"subject_type": "man",
|
||||||
"item_label": "Clothing",
|
"item_label": "Clothing",
|
||||||
"style": "tasteful adult menswear fashion-editorial coloured-pencil comic illustration with casual everyday styling",
|
"style": "tasteful adult menswear fashion-editorial scene with casual everyday styling",
|
||||||
"positive_suffix": "Use crisp clean comic linework, structured fabric texture, detailed hatching, natural light, and tactile textured paper.",
|
"positive_suffix": "Use readable full outfits, structured fabric texture, natural light, coherent anatomy, and polished styling detail.",
|
||||||
"expression_pools": ["men_casual_expressions"],
|
"expression_pools": ["men_casual_expressions"],
|
||||||
"composition_pools": ["men_casual_compositions"],
|
"composition_pools": ["men_casual_compositions"],
|
||||||
"subcategories": [
|
"subcategories": [
|
||||||
@@ -1285,8 +1285,8 @@
|
|||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"subject_type": "couple",
|
"subject_type": "couple",
|
||||||
"item_label": "Clothing",
|
"item_label": "Clothing",
|
||||||
"style": "tasteful adult couple fashion-editorial coloured-pencil comic illustration with coordinated casual styling",
|
"style": "tasteful adult couple fashion-editorial scene with coordinated casual styling",
|
||||||
"positive_suffix": "Use crisp clean comic linework, readable full outfits, detailed hatching, warm natural light, and tactile textured paper.",
|
"positive_suffix": "Use readable coordinated outfits, clear fabric texture, warm natural light, coherent body placement, and polished styling detail.",
|
||||||
"expression_pools": ["couple_casual_expressions"],
|
"expression_pools": ["couple_casual_expressions"],
|
||||||
"composition_pools": ["couple_casual_compositions"],
|
"composition_pools": ["couple_casual_compositions"],
|
||||||
"subcategories": [
|
"subcategories": [
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
"weight": 1.0,
|
"weight": 1.0,
|
||||||
"subject_type": "woman",
|
"subject_type": "woman",
|
||||||
"item_label": "Erotic outfit",
|
"item_label": "Erotic outfit",
|
||||||
"style": "explicit adult erotic fashion illustration, sensual pin-up coloured-pencil comic style, adults only",
|
"style": "explicit adult erotic fashion scene with sensual pin-up styling, adults only",
|
||||||
"positive_suffix": "Use crisp clean comic linework, detailed hatching, soft skin shading, tactile fabric texture, warm intimate lighting, and textured paper.",
|
"positive_suffix": "Use clear adult anatomy, readable erotic outfit construction, tactile fabric texture, warm intimate lighting, coherent body placement, and polished detail.",
|
||||||
"negative_prompt": "minors, childlike appearance, schoolgirl, childlike costume, non-consensual, coercion, violence, injury, watermark",
|
"negative_prompt": "minors, childlike appearance, schoolgirl, childlike costume, non-consensual, coercion, violence, injury, watermark",
|
||||||
"scene_pools": ["softcore_creator_scenes", "mirror_scenes"],
|
"scene_pools": ["softcore_creator_scenes", "mirror_scenes"],
|
||||||
"expression_pools": ["softcore_creator_expressions", "erotic_inviting_expressions"],
|
"expression_pools": ["softcore_creator_expressions", "erotic_inviting_expressions"],
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ Core helper ownership:
|
|||||||
| `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. |
|
| `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. |
|
||||||
| `row_subject_route.py` | Row subject route orchestration, character slot/profile precedence, configured-cast POV labels, visible cast descriptor collection, and descriptor prompt cleanup. |
|
| `row_subject_route.py` | Row subject route orchestration, character slot/profile precedence, configured-cast POV labels, visible cast descriptor collection, and descriptor prompt cleanup. |
|
||||||
| `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. |
|
||||||
|
| `style_config.py` | Visual style preset schemas, style-pool merge behavior, positive style suffixes, and style negative-prompt merging. |
|
||||||
| `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. |
|
||||||
| `row_expression.py` | Row expression cleanup, expression route resolution, expression intensity weighting, character-slot/cast expression override resolution, per-character expression selection, and action-aware character-expression sanitizing. |
|
| `row_expression.py` | Row expression cleanup, expression route resolution, expression intensity weighting, character-slot/cast expression override resolution, per-character expression selection, and action-aware character-expression sanitizing. |
|
||||||
| `row_pools.py` | Row scene/expression/pose/composition pool routing, category inheritance handling, runtime location/composition pool overrides, and generator fallback pools. |
|
| `row_pools.py` | Row scene/expression/pose/composition pool routing, category inheritance handling, runtime location/composition pool overrides, and generator fallback pools. |
|
||||||
@@ -173,9 +174,9 @@ recoverable.
|
|||||||
|
|
||||||
| Node | Important inputs | Important outputs |
|
| Node | Important inputs | Important outputs |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `SxCP Prompt Builder` | category, subcategory, seed, optional config nodes | `prompt`, `negative_prompt`, `caption`, `metadata_json`, `category`, `subcategory` |
|
| `SxCP Prompt Builder` | category, subcategory, seed, optional config nodes including location/composition/style | `prompt`, `negative_prompt`, `caption`, `metadata_json`, `category`, `subcategory` |
|
||||||
| `SxCP Prompt Builder From Configs` | category/cast/profile/filter/config node outputs | Same as `SxCP Prompt Builder` |
|
| `SxCP Prompt Builder From Configs` | category/cast/profile/filter/config node outputs including style_config | Same as `SxCP Prompt Builder` |
|
||||||
| `SxCP Insta/OF Prompt Pair` | options, seed_config, character_cast, location/composition/camera, hardcore_position_config | `softcore_prompt`, `hardcore_prompt`, both negatives, both captions, `shared_descriptor`, `metadata_json` |
|
| `SxCP Insta/OF Prompt Pair` | options, seed_config, character_cast, location/composition/style/camera, hardcore_position_config | `softcore_prompt`, `hardcore_prompt`, both negatives, both captions, `shared_descriptor`, `metadata_json` |
|
||||||
| `SxCP Krea2 Formatter` | `source_text`, connectable `metadata_json`, target | `krea_prompt`, both pair prompts if pair metadata exists, negative outputs, method, `route_trace_json` |
|
| `SxCP Krea2 Formatter` | `source_text`, connectable `metadata_json`, target | `krea_prompt`, both pair prompts if pair metadata exists, negative outputs, method, `route_trace_json` |
|
||||||
| `SxCP SDXL Formatter` | `source_text`, connectable `metadata_json`, target, style/quality preset | `sdxl_prompt`, both pair prompts if pair metadata exists, negative outputs, method, `route_trace_json` |
|
| `SxCP SDXL Formatter` | `source_text`, connectable `metadata_json`, target, style/quality preset | `sdxl_prompt`, both pair prompts if pair metadata exists, negative outputs, method, `route_trace_json` |
|
||||||
| `SxCP Caption Naturalizer` | `source_text`, connectable `metadata_json`, target | `natural_caption`, method, `route_trace_json` |
|
| `SxCP Caption Naturalizer` | `source_text`, connectable `metadata_json`, target | `natural_caption`, method, `route_trace_json` |
|
||||||
@@ -195,6 +196,7 @@ These recipes identify the intended road before editing prompt text.
|
|||||||
| Change only outfit/clothing | Character clothing or category content route | Keep `person_seed`, `scene_seed`, `pose_seed`; change `content_seed`; slot `softcore_outfit` overrides Insta/OF outfit | `SxCP Character Clothing`, `pair_options.py`, category item templates |
|
| Change only outfit/clothing | Character clothing or category content route | Keep `person_seed`, `scene_seed`, `pose_seed`; change `content_seed`; slot `softcore_outfit` overrides Insta/OF outfit | `SxCP Character Clothing`, `pair_options.py`, category item templates |
|
||||||
| Force a custom location | `SxCP Location Pool` or `SxCP Location Theme` -> builder/pair | `combine_mode=replace` to force; `add` to mix with category scenes | `_scene_pool`, `row_location.apply_location_config_to_legacy_row`, camera scene adapter |
|
| Force a custom location | `SxCP Location Pool` or `SxCP Location Theme` -> builder/pair | `combine_mode=replace` to force; `add` to mix with category scenes | `_scene_pool`, `row_location.apply_location_config_to_legacy_row`, camera scene adapter |
|
||||||
| Force a custom frame/composition | `SxCP Composition Pool` or `SxCP Location Theme` -> builder/pair | `combine_mode=replace` to force; `add` to mix | `_composition_pool`, `row_location.apply_composition_config_to_legacy_row`, Krea composition phrase |
|
| Force a custom frame/composition | `SxCP Composition Pool` or `SxCP Location Theme` -> builder/pair | `combine_mode=replace` to force; `add` to mix | `_composition_pool`, `row_location.apply_composition_config_to_legacy_row`, Krea composition phrase |
|
||||||
|
| Force realistic/photo/comic style | `SxCP Style Pool` -> builder/pair/Scene Start | `combine_mode=replace` to override category style; `add` to append; choose realistic/photo/comic preset | `style_config.py`, `row_rendering.resolve_row_text_fields`, row `style` / `positive_suffix` metadata |
|
||||||
| Use Qwen/orbit camera geometry | Qwen/orbit node -> camera_config -> builder/pair | For pair, use `softcore_camera_config` and/or `hardcore_camera_config`; set mode from config in options | `_camera_config_with_mode`, `_camera_directive`, `_camera_scene_directive_for_context` |
|
| Use Qwen/orbit camera geometry | Qwen/orbit node -> camera_config -> builder/pair | For pair, use `softcore_camera_config` and/or `hardcore_camera_config`; set mode from config in options | `_camera_config_with_mode`, `_camera_directive`, `_camera_scene_directive_for_context` |
|
||||||
| Use Krea2 for only hard prompt from a pair | Pair `metadata_json` -> Krea2 Formatter | `target=hardcore`, `input_hint=metadata_json` or auto with metadata connected | `_insta_pair_to_krea`, hard row fields |
|
| Use Krea2 for only hard prompt from a pair | Pair `metadata_json` -> Krea2 Formatter | `target=hardcore`, `input_hint=metadata_json` or auto with metadata connected | `_insta_pair_to_krea`, hard row fields |
|
||||||
| Convert builder output to SDXL tags | Builder/pair metadata -> SDXL Formatter | Use metadata input; set `target`; select style and quality preset | `sdxl_tag_routes.py`, `sdxl_tag_policy.py`, compatibility wrappers `_row_core_tags` / `_soft_tags` / `_hard_tags` |
|
| Convert builder output to SDXL tags | Builder/pair metadata -> SDXL Formatter | Use metadata input; set `target`; select style and quality preset | `sdxl_tag_routes.py`, `sdxl_tag_policy.py`, compatibility wrappers `_row_core_tags` / `_soft_tags` / `_hard_tags` |
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ SXCP_COMPOSITION_CONFIG = "SXCP_COMPOSITION_CONFIG"
|
|||||||
SXCP_CATEGORY_CONFIG = "SXCP_CATEGORY_CONFIG"
|
SXCP_CATEGORY_CONFIG = "SXCP_CATEGORY_CONFIG"
|
||||||
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
||||||
SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE"
|
SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE"
|
||||||
|
SXCP_STYLE_CONFIG = "SXCP_STYLE_CONFIG"
|
||||||
SXCP_HARDCORE_POSITION_CONFIG = "SXCP_HARDCORE_POSITION_CONFIG"
|
SXCP_HARDCORE_POSITION_CONFIG = "SXCP_HARDCORE_POSITION_CONFIG"
|
||||||
SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
|
SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
|
||||||
SXCP_CHARACTER_PROFILE = "SXCP_CHARACTER_PROFILE"
|
SXCP_CHARACTER_PROFILE = "SXCP_CHARACTER_PROFILE"
|
||||||
@@ -64,6 +65,7 @@ class SxCPPromptBuilder:
|
|||||||
"camera_config": (SXCP_CAMERA_CONFIG,),
|
"camera_config": (SXCP_CAMERA_CONFIG,),
|
||||||
"location_config": (SXCP_LOCATION_CONFIG,),
|
"location_config": (SXCP_LOCATION_CONFIG,),
|
||||||
"composition_config": (SXCP_COMPOSITION_CONFIG,),
|
"composition_config": (SXCP_COMPOSITION_CONFIG,),
|
||||||
|
"style_config": (SXCP_STYLE_CONFIG,),
|
||||||
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
||||||
"character_cast": (SXCP_CHARACTER_CAST,),
|
"character_cast": (SXCP_CHARACTER_CAST,),
|
||||||
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
||||||
@@ -101,6 +103,7 @@ class SxCPPromptBuilder:
|
|||||||
camera_config="",
|
camera_config="",
|
||||||
location_config="",
|
location_config="",
|
||||||
composition_config="",
|
composition_config="",
|
||||||
|
style_config="",
|
||||||
character_profile="",
|
character_profile="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
hardcore_position_config="",
|
hardcore_position_config="",
|
||||||
@@ -137,6 +140,7 @@ class SxCPPromptBuilder:
|
|||||||
camera_config=camera_config or "",
|
camera_config=camera_config or "",
|
||||||
location_config=location_config or "",
|
location_config=location_config or "",
|
||||||
composition_config=composition_config or "",
|
composition_config=composition_config or "",
|
||||||
|
style_config=style_config or "",
|
||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
@@ -170,6 +174,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
"camera_config": (SXCP_CAMERA_CONFIG,),
|
"camera_config": (SXCP_CAMERA_CONFIG,),
|
||||||
"location_config": (SXCP_LOCATION_CONFIG,),
|
"location_config": (SXCP_LOCATION_CONFIG,),
|
||||||
"composition_config": (SXCP_COMPOSITION_CONFIG,),
|
"composition_config": (SXCP_COMPOSITION_CONFIG,),
|
||||||
|
"style_config": (SXCP_STYLE_CONFIG,),
|
||||||
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
||||||
"character_cast": (SXCP_CHARACTER_CAST,),
|
"character_cast": (SXCP_CHARACTER_CAST,),
|
||||||
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
||||||
@@ -197,6 +202,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
camera_config="",
|
camera_config="",
|
||||||
location_config="",
|
location_config="",
|
||||||
composition_config="",
|
composition_config="",
|
||||||
|
style_config="",
|
||||||
character_profile="",
|
character_profile="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
hardcore_position_config="",
|
hardcore_position_config="",
|
||||||
@@ -215,6 +221,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
camera_config=camera_config or "",
|
camera_config=camera_config or "",
|
||||||
location_config=location_config or "",
|
location_config=location_config or "",
|
||||||
composition_config=composition_config or "",
|
composition_config=composition_config or "",
|
||||||
|
style_config=style_config or "",
|
||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
|
|||||||
SXCP_CHARACTER_PROFILE = "SXCP_CHARACTER_PROFILE"
|
SXCP_CHARACTER_PROFILE = "SXCP_CHARACTER_PROFILE"
|
||||||
SXCP_ETHNICITY_LIST = "SXCP_ETHNICITY_LIST"
|
SXCP_ETHNICITY_LIST = "SXCP_ETHNICITY_LIST"
|
||||||
SXCP_FILTER_CONFIG = "SXCP_FILTER_CONFIG"
|
SXCP_FILTER_CONFIG = "SXCP_FILTER_CONFIG"
|
||||||
|
SXCP_STYLE_CONFIG = "SXCP_STYLE_CONFIG"
|
||||||
|
|
||||||
|
|
||||||
class SxCPInstaOFOptions:
|
class SxCPInstaOFOptions:
|
||||||
@@ -130,6 +131,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
"hardcore_camera_config": (SXCP_CAMERA_CONFIG,),
|
"hardcore_camera_config": (SXCP_CAMERA_CONFIG,),
|
||||||
"location_config": (SXCP_LOCATION_CONFIG,),
|
"location_config": (SXCP_LOCATION_CONFIG,),
|
||||||
"composition_config": (SXCP_COMPOSITION_CONFIG,),
|
"composition_config": (SXCP_COMPOSITION_CONFIG,),
|
||||||
|
"style_config": (SXCP_STYLE_CONFIG,),
|
||||||
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
||||||
"character_cast": (SXCP_CHARACTER_CAST,),
|
"character_cast": (SXCP_CHARACTER_CAST,),
|
||||||
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
||||||
@@ -170,6 +172,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
hardcore_camera_config="",
|
hardcore_camera_config="",
|
||||||
location_config="",
|
location_config="",
|
||||||
composition_config="",
|
composition_config="",
|
||||||
|
style_config="",
|
||||||
character_profile="",
|
character_profile="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
hardcore_position_config="",
|
hardcore_position_config="",
|
||||||
@@ -196,6 +199,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
hardcore_camera_config=hardcore_camera_config or "",
|
hardcore_camera_config=hardcore_camera_config or "",
|
||||||
location_config=location_config or "",
|
location_config=location_config or "",
|
||||||
composition_config=composition_config or "",
|
composition_config=composition_config or "",
|
||||||
|
style_config=style_config or "",
|
||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ try:
|
|||||||
location_pool_preset_choices,
|
location_pool_preset_choices,
|
||||||
location_theme_choices,
|
location_theme_choices,
|
||||||
)
|
)
|
||||||
|
from .style_config import (
|
||||||
|
build_style_config_json,
|
||||||
|
style_combine_mode_choices,
|
||||||
|
style_pool_preset_choices,
|
||||||
|
)
|
||||||
except ImportError: # Allows local smoke tests from the repository root.
|
except ImportError: # Allows local smoke tests from the repository root.
|
||||||
from category_cast_config import (
|
from category_cast_config import (
|
||||||
build_cast_config_json,
|
build_cast_config_json,
|
||||||
@@ -41,6 +46,11 @@ except ImportError: # Allows local smoke tests from the repository root.
|
|||||||
location_pool_preset_choices,
|
location_pool_preset_choices,
|
||||||
location_theme_choices,
|
location_theme_choices,
|
||||||
)
|
)
|
||||||
|
from style_config import (
|
||||||
|
build_style_config_json,
|
||||||
|
style_combine_mode_choices,
|
||||||
|
style_pool_preset_choices,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
SXCP_CATEGORY_CONFIG = "SXCP_CATEGORY_CONFIG"
|
SXCP_CATEGORY_CONFIG = "SXCP_CATEGORY_CONFIG"
|
||||||
@@ -48,6 +58,7 @@ SXCP_LOCATION_CONFIG = "SXCP_LOCATION_CONFIG"
|
|||||||
SXCP_COMPOSITION_CONFIG = "SXCP_COMPOSITION_CONFIG"
|
SXCP_COMPOSITION_CONFIG = "SXCP_COMPOSITION_CONFIG"
|
||||||
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
||||||
SXCP_SEED_CONFIG = "SXCP_SEED_CONFIG"
|
SXCP_SEED_CONFIG = "SXCP_SEED_CONFIG"
|
||||||
|
SXCP_STYLE_CONFIG = "SXCP_STYLE_CONFIG"
|
||||||
|
|
||||||
|
|
||||||
class SxCPCategoryPreset:
|
class SxCPCategoryPreset:
|
||||||
@@ -178,6 +189,51 @@ class SxCPLocationTheme:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SxCPStylePool:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"enabled": ("BOOLEAN", {"default": True}),
|
||||||
|
"combine_mode": (style_combine_mode_choices(), {"default": "replace"}),
|
||||||
|
"preset": (style_pool_preset_choices(), {"default": "realistic_photo"}),
|
||||||
|
"custom_style": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"custom_positive_suffix": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"custom_negative": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"style_config": (SXCP_STYLE_CONFIG,),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = (SXCP_STYLE_CONFIG, "STRING")
|
||||||
|
RETURN_NAMES = ("style_config", "summary")
|
||||||
|
FUNCTION = "build"
|
||||||
|
CATEGORY = "prompt_builder"
|
||||||
|
|
||||||
|
def build(
|
||||||
|
self,
|
||||||
|
enabled,
|
||||||
|
combine_mode,
|
||||||
|
preset,
|
||||||
|
custom_style,
|
||||||
|
custom_positive_suffix,
|
||||||
|
custom_negative,
|
||||||
|
style_config="",
|
||||||
|
):
|
||||||
|
config = build_style_config_json(
|
||||||
|
enabled=enabled,
|
||||||
|
combine_mode=combine_mode,
|
||||||
|
preset=preset,
|
||||||
|
custom_style=custom_style or "",
|
||||||
|
custom_positive_suffix=custom_positive_suffix or "",
|
||||||
|
custom_negative=custom_negative or "",
|
||||||
|
style_config=style_config or "",
|
||||||
|
)
|
||||||
|
parsed = json.loads(config)
|
||||||
|
return config, parsed.get("summary", "")
|
||||||
|
|
||||||
|
|
||||||
class SxCPCastControl:
|
class SxCPCastControl:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -313,6 +369,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"SxCPLocationPool": SxCPLocationPool,
|
"SxCPLocationPool": SxCPLocationPool,
|
||||||
"SxCPCompositionPool": SxCPCompositionPool,
|
"SxCPCompositionPool": SxCPCompositionPool,
|
||||||
"SxCPLocationTheme": SxCPLocationTheme,
|
"SxCPLocationTheme": SxCPLocationTheme,
|
||||||
|
"SxCPStylePool": SxCPStylePool,
|
||||||
"SxCPCastControl": SxCPCastControl,
|
"SxCPCastControl": SxCPCastControl,
|
||||||
"SxCPCastBias": SxCPCastBias,
|
"SxCPCastBias": SxCPCastBias,
|
||||||
}
|
}
|
||||||
@@ -322,6 +379,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"SxCPLocationPool": "SxCP Location Pool",
|
"SxCPLocationPool": "SxCP Location Pool",
|
||||||
"SxCPCompositionPool": "SxCP Composition Pool",
|
"SxCPCompositionPool": "SxCP Composition Pool",
|
||||||
"SxCPLocationTheme": "SxCP Location Theme",
|
"SxCPLocationTheme": "SxCP Location Theme",
|
||||||
|
"SxCPStylePool": "SxCP Style Pool",
|
||||||
"SxCPCastControl": "SxCP Cast Control",
|
"SxCPCastControl": "SxCP Cast Control",
|
||||||
"SxCPCastBias": "SxCP Cast Bias",
|
"SxCPCastBias": "SxCP Cast Bias",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ SXCP_CATEGORY_CONFIG = "SXCP_CATEGORY_CONFIG"
|
|||||||
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
||||||
SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE"
|
SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE"
|
||||||
SXCP_FILTER_CONFIG = "SXCP_FILTER_CONFIG"
|
SXCP_FILTER_CONFIG = "SXCP_FILTER_CONFIG"
|
||||||
|
SXCP_STYLE_CONFIG = "SXCP_STYLE_CONFIG"
|
||||||
SXCP_ETHNICITY_LIST = "SXCP_ETHNICITY_LIST"
|
SXCP_ETHNICITY_LIST = "SXCP_ETHNICITY_LIST"
|
||||||
SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
|
SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
|
||||||
SXCP_CHARACTER_SLOT = "SXCP_CHARACTER_SLOT"
|
SXCP_CHARACTER_SLOT = "SXCP_CHARACTER_SLOT"
|
||||||
@@ -647,6 +648,7 @@ def _compat_configs(scene: dict[str, Any], branch_name: str = "") -> dict[str, A
|
|||||||
"camera_config": branch_configs.get("camera_config") or configs.get("camera_config") or "",
|
"camera_config": branch_configs.get("camera_config") or configs.get("camera_config") or "",
|
||||||
"location_config": branch_configs.get("location_config") or configs.get("location_config") or "",
|
"location_config": branch_configs.get("location_config") or configs.get("location_config") or "",
|
||||||
"composition_config": branch_configs.get("composition_config") or configs.get("composition_config") or "",
|
"composition_config": branch_configs.get("composition_config") or configs.get("composition_config") or "",
|
||||||
|
"style_config": branch_configs.get("style_config") or configs.get("style_config") or "",
|
||||||
"character_profile": branch_configs.get("character_profile") or configs.get("character_profile") or "",
|
"character_profile": branch_configs.get("character_profile") or configs.get("character_profile") or "",
|
||||||
"character_cast": branch_configs.get("character_cast") or configs.get("character_cast") or "",
|
"character_cast": branch_configs.get("character_cast") or configs.get("character_cast") or "",
|
||||||
"hardcore_position_config": branch_configs.get("hardcore_position_config") or configs.get("hardcore_position_config") or "",
|
"hardcore_position_config": branch_configs.get("hardcore_position_config") or configs.get("hardcore_position_config") or "",
|
||||||
@@ -1210,6 +1212,7 @@ class SxCPSceneStart:
|
|||||||
"category_config": (SXCP_CATEGORY_CONFIG,),
|
"category_config": (SXCP_CATEGORY_CONFIG,),
|
||||||
"generation_profile": (SXCP_GENERATION_PROFILE,),
|
"generation_profile": (SXCP_GENERATION_PROFILE,),
|
||||||
"filter_config": (SXCP_FILTER_CONFIG,),
|
"filter_config": (SXCP_FILTER_CONFIG,),
|
||||||
|
"style_config": (SXCP_STYLE_CONFIG,),
|
||||||
"extra_positive": ("STRING", {"default": "", "multiline": True}),
|
"extra_positive": ("STRING", {"default": "", "multiline": True}),
|
||||||
"extra_negative": ("STRING", {"default": "", "multiline": True}),
|
"extra_negative": ("STRING", {"default": "", "multiline": True}),
|
||||||
},
|
},
|
||||||
@@ -1235,6 +1238,7 @@ class SxCPSceneStart:
|
|||||||
category_config="",
|
category_config="",
|
||||||
generation_profile="",
|
generation_profile="",
|
||||||
filter_config="",
|
filter_config="",
|
||||||
|
style_config="",
|
||||||
extra_positive="",
|
extra_positive="",
|
||||||
extra_negative="",
|
extra_negative="",
|
||||||
):
|
):
|
||||||
@@ -1258,6 +1262,7 @@ class SxCPSceneStart:
|
|||||||
_set_config(scene, "category_config", category_config)
|
_set_config(scene, "category_config", category_config)
|
||||||
_set_config(scene, "generation_profile", generation_profile)
|
_set_config(scene, "generation_profile", generation_profile)
|
||||||
_set_config(scene, "filter_config", filter_config)
|
_set_config(scene, "filter_config", filter_config)
|
||||||
|
_set_config(scene, "style_config", style_config)
|
||||||
_add_history(scene, "scene_start", f"{category_preset}/{subcategory}; {profile}")
|
_add_history(scene, "scene_start", f"{category_preset}/{subcategory}; {profile}")
|
||||||
return _scene_out(scene)
|
return _scene_out(scene)
|
||||||
|
|
||||||
@@ -2220,6 +2225,7 @@ class SxCPSceneOutput:
|
|||||||
hardcore_position_config=configs["hardcore_position_config"],
|
hardcore_position_config=configs["hardcore_position_config"],
|
||||||
location_config=configs["location_config"],
|
location_config=configs["location_config"],
|
||||||
composition_config=configs["composition_config"],
|
composition_config=configs["composition_config"],
|
||||||
|
style_config=configs["style_config"],
|
||||||
extra_positive=configs["extra_positive"],
|
extra_positive=configs["extra_positive"],
|
||||||
extra_negative=configs["extra_negative"],
|
extra_negative=configs["extra_negative"],
|
||||||
)
|
)
|
||||||
@@ -2289,6 +2295,7 @@ class SxCPScenePairOutput:
|
|||||||
hardcore_position_config=hard_configs["hardcore_position_config"],
|
hardcore_position_config=hard_configs["hardcore_position_config"],
|
||||||
location_config=base_configs["location_config"] or hard_configs["location_config"],
|
location_config=base_configs["location_config"] or hard_configs["location_config"],
|
||||||
composition_config=base_configs["composition_config"] or hard_configs["composition_config"],
|
composition_config=base_configs["composition_config"] or hard_configs["composition_config"],
|
||||||
|
style_config=base_configs["style_config"] or hard_configs["style_config"],
|
||||||
extra_positive=_joined_text(base_configs["extra_positive"], hard_configs["extra_positive"]),
|
extra_positive=_joined_text(base_configs["extra_positive"], hard_configs["extra_positive"]),
|
||||||
extra_negative=base_configs["extra_negative"] or hard_configs["extra_negative"],
|
extra_negative=base_configs["extra_negative"] or hard_configs["extra_negative"],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ COMMON_INPUT_TOOLTIPS = {
|
|||||||
"camera_config": "Camera config consumed only by nodes/options set to from_camera_config.",
|
"camera_config": "Camera config consumed only by nodes/options set to from_camera_config.",
|
||||||
"location_config": "Location config from SxCP Location Pool. It can replace or add to the category scene pool.",
|
"location_config": "Location config from SxCP Location Pool. It can replace or add to the category scene pool.",
|
||||||
"composition_config": "Composition config from SxCP Composition Pool or Location Theme. It can replace or add framing options.",
|
"composition_config": "Composition config from SxCP Composition Pool or Location Theme. It can replace or add framing options.",
|
||||||
|
"style_config": "Visual style config from SxCP Style Pool. It controls realistic/photo/comic rendering separately from category, action, and pose logic.",
|
||||||
"softcore_camera_config": "Camera config used only for the softcore Insta/OF prompt. Falls back to camera_config if empty.",
|
"softcore_camera_config": "Camera config used only for the softcore Insta/OF prompt. Falls back to camera_config if empty.",
|
||||||
"hardcore_camera_config": "Camera config used only for the hardcore Insta/OF prompt. Falls back to camera_config if empty.",
|
"hardcore_camera_config": "Camera config used only for the hardcore Insta/OF prompt. Falls back to camera_config if empty.",
|
||||||
"character_profile": "Saved or loaded single-character profile. Character slots override this for configured casts.",
|
"character_profile": "Saved or loaded single-character profile. Character slots override this for configured casts.",
|
||||||
@@ -32,6 +33,9 @@ COMMON_INPUT_TOOLTIPS = {
|
|||||||
"hardcore_position_config": "Hardcore action/position config. Chain Position Pool into Action Filter, then into the generator.",
|
"hardcore_position_config": "Hardcore action/position config. Chain Position Pool into Action Filter, then into the generator.",
|
||||||
"custom_locations": "One custom location per line. Use plain text, or slug: location text.",
|
"custom_locations": "One custom location per line. Use plain text, or slug: location text.",
|
||||||
"custom_compositions": "One custom composition/framing phrase per line.",
|
"custom_compositions": "One custom composition/framing phrase per line.",
|
||||||
|
"custom_style": "Manual visual style phrase. Use this when the preset list is not specific enough.",
|
||||||
|
"custom_positive_suffix": "Manual style/quality suffix merged with the selected style preset.",
|
||||||
|
"custom_negative": "Negative style terms added by the style pool.",
|
||||||
"theme": "Matched location and composition theme, useful when the place needs compatible framing.",
|
"theme": "Matched location and composition theme, useful when the place needs compatible framing.",
|
||||||
"metadata_json": "Structured metadata from an SxCP generator. Prefer this over raw prompt text for formatters and profile save.",
|
"metadata_json": "Structured metadata from an SxCP generator. Prefer this over raw prompt text for formatters and profile save.",
|
||||||
"scene": "Structured v2 scene context. Chain Scene nodes in order, then connect to Scene Output or Scene Pair Output.",
|
"scene": "Structured v2 scene context. Chain Scene nodes in order, then connect to Scene Output or Scene Pair Output.",
|
||||||
@@ -304,6 +308,15 @@ NODE_INPUT_TOOLTIPS = {
|
|||||||
"phone_visibility": "Leave auto when using Qwen/Orbit camera prompts unless you explicitly want phone visibility text.",
|
"phone_visibility": "Leave auto when using Qwen/Orbit camera prompts unless you explicitly want phone visibility text.",
|
||||||
"suppress_phone_visibility": "Avoid adding phone visibility text unless you explicitly set a phone option.",
|
"suppress_phone_visibility": "Avoid adding phone visibility text unless you explicitly set a phone option.",
|
||||||
},
|
},
|
||||||
|
"SxCPStylePool": {
|
||||||
|
"enabled": "Disable to keep the node wired while preserving category/default style behavior.",
|
||||||
|
"combine_mode": "replace overrides category style; add appends this visual style to incoming/category style; disabled emits no style override.",
|
||||||
|
"preset": "Visual rendering preset only. It does not select content, pose, exposure, or camera.",
|
||||||
|
"style_config": "Optional incoming style config. Use combine_mode=add to chain multiple style nodes.",
|
||||||
|
"custom_style": "Manual visual style phrase, for example realistic phone photo or colored-pencil pin-up.",
|
||||||
|
"custom_positive_suffix": "Extra rendering/detail sentence added to the prompt when the style is active.",
|
||||||
|
"custom_negative": "Negative style terms merged into the generated negative prompt.",
|
||||||
|
},
|
||||||
"SxCPHardcorePositionPool": {
|
"SxCPHardcorePositionPool": {
|
||||||
"family": "Restrict the broad hardcore family. Use any when you want oral and penetration to both be possible.",
|
"family": "Restrict the broad hardcore family. Use any when you want oral and penetration to both be possible.",
|
||||||
"combine_mode": "replace discards incoming position choices; add merges these choices with the incoming config.",
|
"combine_mode": "replace discards incoming position choices; add merges these choices with the incoming config.",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class InstaPairBuildRequest:
|
|||||||
hardcore_position_config: str | dict[str, Any] | None = ""
|
hardcore_position_config: str | dict[str, Any] | None = ""
|
||||||
location_config: str | dict[str, Any] | None = ""
|
location_config: str | dict[str, Any] | None = ""
|
||||||
composition_config: str | dict[str, Any] | None = ""
|
composition_config: str | dict[str, Any] | None = ""
|
||||||
|
style_config: str | dict[str, Any] | None = ""
|
||||||
extra_positive: str = ""
|
extra_positive: str = ""
|
||||||
extra_negative: str = ""
|
extra_negative: str = ""
|
||||||
|
|
||||||
@@ -148,6 +149,7 @@ def build_insta_of_pair(request: InstaPairBuildRequest, deps: InstaPairBuildDepe
|
|||||||
hardcore_position_config=request.hardcore_position_config,
|
hardcore_position_config=request.hardcore_position_config,
|
||||||
location_config=request.location_config or "",
|
location_config=request.location_config or "",
|
||||||
composition_config=request.composition_config or "",
|
composition_config=request.composition_config or "",
|
||||||
|
style_config=request.style_config or "",
|
||||||
build_prompt=deps.build_prompt,
|
build_prompt=deps.build_prompt,
|
||||||
axis_rng=deps.axis_rng,
|
axis_rng=deps.axis_rng,
|
||||||
cast_expression_intensity_override=deps.cast_expression_intensity_override,
|
cast_expression_intensity_override=deps.cast_expression_intensity_override,
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ def build_insta_pair_rows_result(
|
|||||||
softcore_item_prompt_label: Callable[[str], str],
|
softcore_item_prompt_label: Callable[[str], str],
|
||||||
pov_prompt_directive: Callable[[list[str]], str],
|
pov_prompt_directive: Callable[[list[str]], str],
|
||||||
pov_composition_prompt: Callable[[Any, list[str]], str],
|
pov_composition_prompt: Callable[[Any, list[str]], str],
|
||||||
|
style_config: str | dict[str, Any] | None = "",
|
||||||
) -> InstaPairRowsRoute:
|
) -> InstaPairRowsRoute:
|
||||||
soft_content_rng = axis_rng(parsed_seed_config, "content", seed, row_number + 311)
|
soft_content_rng = axis_rng(parsed_seed_config, "content", seed, row_number + 311)
|
||||||
hard_content_rng = axis_rng(parsed_seed_config, "content", seed, row_number + 317)
|
hard_content_rng = axis_rng(parsed_seed_config, "content", seed, row_number + 317)
|
||||||
@@ -131,6 +132,7 @@ def build_insta_pair_rows_result(
|
|||||||
character_cast="",
|
character_cast="",
|
||||||
location_config=location_config or "",
|
location_config=location_config or "",
|
||||||
composition_config=composition_config or "",
|
composition_config=composition_config or "",
|
||||||
|
style_config=style_config or "",
|
||||||
)
|
)
|
||||||
soft_row["expression_intensity_source"] = soft_expression_intensity_source
|
soft_row["expression_intensity_source"] = soft_expression_intensity_source
|
||||||
if primary_slot_context:
|
if primary_slot_context:
|
||||||
@@ -196,6 +198,7 @@ def build_insta_pair_rows_result(
|
|||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
location_config=location_config or "",
|
location_config=location_config or "",
|
||||||
composition_config=composition_config or "",
|
composition_config=composition_config or "",
|
||||||
|
style_config=style_config or "",
|
||||||
)
|
)
|
||||||
hard_row["hardcore_detail_density"] = options["hardcore_detail_density"]
|
hard_row["hardcore_detail_density"] = options["hardcore_detail_density"]
|
||||||
hard_row["pov_character_labels"] = pov_character_labels
|
hard_row["pov_character_labels"] = pov_character_labels
|
||||||
@@ -248,6 +251,7 @@ def build_insta_pair_rows(
|
|||||||
softcore_item_prompt_label: Callable[[str], str],
|
softcore_item_prompt_label: Callable[[str], str],
|
||||||
pov_prompt_directive: Callable[[list[str]], str],
|
pov_prompt_directive: Callable[[list[str]], str],
|
||||||
pov_composition_prompt: Callable[[Any, list[str]], str],
|
pov_composition_prompt: Callable[[Any, list[str]], str],
|
||||||
|
style_config: str | dict[str, Any] | None = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
return build_insta_pair_rows_result(
|
return build_insta_pair_rows_result(
|
||||||
row_number=row_number,
|
row_number=row_number,
|
||||||
@@ -273,6 +277,7 @@ def build_insta_pair_rows(
|
|||||||
hardcore_position_config=hardcore_position_config,
|
hardcore_position_config=hardcore_position_config,
|
||||||
location_config=location_config,
|
location_config=location_config,
|
||||||
composition_config=composition_config,
|
composition_config=composition_config,
|
||||||
|
style_config=style_config,
|
||||||
build_prompt=build_prompt,
|
build_prompt=build_prompt,
|
||||||
axis_rng=axis_rng,
|
axis_rng=axis_rng,
|
||||||
cast_expression_intensity_override=cast_expression_intensity_override,
|
cast_expression_intensity_override=cast_expression_intensity_override,
|
||||||
|
|||||||
+45
-2
@@ -47,6 +47,7 @@ try:
|
|||||||
from . import row_route_metadata as row_route_policy
|
from . import row_route_metadata as row_route_policy
|
||||||
from . import row_subject_route as row_subject_route_policy
|
from . import row_subject_route as row_subject_route_policy
|
||||||
from . import seed_config as seed_policy
|
from . import seed_config as seed_policy
|
||||||
|
from . import style_config as style_policy
|
||||||
from . import subject_context as subject_context_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,
|
||||||
@@ -95,6 +96,7 @@ except ImportError: # Allows local smoke tests with `python -c`.
|
|||||||
import row_route_metadata as row_route_policy
|
import row_route_metadata as row_route_policy
|
||||||
import row_subject_route as row_subject_route_policy
|
import row_subject_route as row_subject_route_policy
|
||||||
import seed_config as seed_policy
|
import seed_config as seed_policy
|
||||||
|
import style_config as style_policy
|
||||||
import subject_context as subject_context_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,
|
||||||
@@ -376,6 +378,7 @@ CATEGORY_PRESETS = category_cast_policy.CATEGORY_PRESETS
|
|||||||
CAST_PRESETS = category_cast_policy.CAST_PRESETS
|
CAST_PRESETS = category_cast_policy.CAST_PRESETS
|
||||||
|
|
||||||
GENERATION_PROFILE_PRESETS = generation_profile_policy.GENERATION_PROFILE_PRESETS
|
GENERATION_PROFILE_PRESETS = generation_profile_policy.GENERATION_PROFILE_PRESETS
|
||||||
|
STYLE_PRESETS = style_policy.STYLE_PRESETS
|
||||||
|
|
||||||
|
|
||||||
def category_preset_choices() -> list[str]:
|
def category_preset_choices() -> list[str]:
|
||||||
@@ -390,6 +393,14 @@ def generation_profile_choices() -> list[str]:
|
|||||||
return generation_profile_policy.generation_profile_choices()
|
return generation_profile_policy.generation_profile_choices()
|
||||||
|
|
||||||
|
|
||||||
|
def style_pool_preset_choices() -> list[str]:
|
||||||
|
return style_policy.style_pool_preset_choices()
|
||||||
|
|
||||||
|
|
||||||
|
def style_combine_mode_choices() -> list[str]:
|
||||||
|
return style_policy.style_combine_mode_choices()
|
||||||
|
|
||||||
|
|
||||||
def build_category_config_json(preset: str = "auto_weighted", subcategory: str = RANDOM_SUBCATEGORY) -> str:
|
def build_category_config_json(preset: str = "auto_weighted", subcategory: str = RANDOM_SUBCATEGORY) -> str:
|
||||||
return category_cast_policy.build_category_config_json(preset=preset, subcategory=subcategory)
|
return category_cast_policy.build_category_config_json(preset=preset, subcategory=subcategory)
|
||||||
|
|
||||||
@@ -436,6 +447,30 @@ def _parse_generation_profile(profile_config: str | dict[str, Any] | None) -> di
|
|||||||
return generation_profile_policy.parse_generation_profile(profile_config)
|
return generation_profile_policy.parse_generation_profile(profile_config)
|
||||||
|
|
||||||
|
|
||||||
|
def build_style_config_json(
|
||||||
|
enabled: bool = True,
|
||||||
|
combine_mode: str = "replace",
|
||||||
|
preset: str = "category_default",
|
||||||
|
custom_style: str = "",
|
||||||
|
custom_positive_suffix: str = "",
|
||||||
|
custom_negative: str = "",
|
||||||
|
style_config: str | dict[str, Any] | None = "",
|
||||||
|
) -> str:
|
||||||
|
return style_policy.build_style_config_json(
|
||||||
|
enabled=enabled,
|
||||||
|
combine_mode=combine_mode,
|
||||||
|
preset=preset,
|
||||||
|
custom_style=custom_style,
|
||||||
|
custom_positive_suffix=custom_positive_suffix,
|
||||||
|
custom_negative=custom_negative,
|
||||||
|
style_config=style_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_style_config(style_config: str | dict[str, Any] | None) -> dict[str, Any]:
|
||||||
|
return style_policy.parse_style_config(style_config)
|
||||||
|
|
||||||
|
|
||||||
def build_filter_config_json(
|
def build_filter_config_json(
|
||||||
ethnicity: str = "any",
|
ethnicity: str = "any",
|
||||||
figure: str = "curvy",
|
figure: str = "curvy",
|
||||||
@@ -880,8 +915,9 @@ def _row_text_fields(
|
|||||||
category: dict[str, Any],
|
category: dict[str, Any],
|
||||||
subcategory: dict[str, Any],
|
subcategory: dict[str, Any],
|
||||||
item: Any,
|
item: Any,
|
||||||
|
style_config: str | dict[str, Any] | None = None,
|
||||||
) -> row_rendering_policy.RowTextFields:
|
) -> row_rendering_policy.RowTextFields:
|
||||||
return row_rendering_policy.resolve_row_text_fields(category, subcategory, item)
|
return row_rendering_policy.resolve_row_text_fields(category, subcategory, item, style_config)
|
||||||
|
|
||||||
|
|
||||||
def _clean_prompt_punctuation(text: str) -> str:
|
def _clean_prompt_punctuation(text: str) -> str:
|
||||||
@@ -2284,6 +2320,7 @@ def _build_custom_row(
|
|||||||
hardcore_position_config: str | dict[str, Any] | None = None,
|
hardcore_position_config: str | dict[str, Any] | None = None,
|
||||||
location_config: str | dict[str, Any] | None = None,
|
location_config: str | dict[str, Any] | None = None,
|
||||||
composition_config: str | dict[str, Any] | None = None,
|
composition_config: str | dict[str, Any] | None = None,
|
||||||
|
style_config: str | dict[str, Any] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
scene_rng = _axis_rng(seed_config, "scene", seed, row_number)
|
scene_rng = _axis_rng(seed_config, "scene", seed, row_number)
|
||||||
pose_rng = _axis_rng(seed_config, "pose", seed, row_number)
|
pose_rng = _axis_rng(seed_config, "pose", seed, row_number)
|
||||||
@@ -2421,7 +2458,7 @@ def _build_custom_row(
|
|||||||
position_key = action_route.position_key
|
position_key = action_route.position_key
|
||||||
action_family = action_route.action_family
|
action_family = action_route.action_family
|
||||||
|
|
||||||
text_fields = _row_text_fields(category, subcategory, item)
|
text_fields = _row_text_fields(category, subcategory, item, style_config)
|
||||||
|
|
||||||
assembly_request = row_assembly_policy.CustomRowAssemblyRequest(
|
assembly_request = row_assembly_policy.CustomRowAssemblyRequest(
|
||||||
row_number=row_number,
|
row_number=row_number,
|
||||||
@@ -2542,6 +2579,7 @@ def build_prompt(
|
|||||||
hardcore_position_config: str | dict[str, Any] | None = None,
|
hardcore_position_config: str | dict[str, Any] | None = None,
|
||||||
location_config: str | dict[str, Any] | None = None,
|
location_config: str | dict[str, Any] | None = None,
|
||||||
composition_config: str | dict[str, Any] | None = None,
|
composition_config: str | dict[str, Any] | None = None,
|
||||||
|
style_config: str | dict[str, Any] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
return builder_prompt_route_policy.build_prompt(
|
return builder_prompt_route_policy.build_prompt(
|
||||||
builder_prompt_route_policy.PromptBuildRequest(
|
builder_prompt_route_policy.PromptBuildRequest(
|
||||||
@@ -2575,6 +2613,7 @@ def build_prompt(
|
|||||||
hardcore_position_config=hardcore_position_config,
|
hardcore_position_config=hardcore_position_config,
|
||||||
location_config=location_config,
|
location_config=location_config,
|
||||||
composition_config=composition_config,
|
composition_config=composition_config,
|
||||||
|
style_config=style_config,
|
||||||
),
|
),
|
||||||
_prompt_build_dependencies(),
|
_prompt_build_dependencies(),
|
||||||
)
|
)
|
||||||
@@ -2605,6 +2644,7 @@ def build_prompt_from_configs(
|
|||||||
hardcore_position_config: str | dict[str, Any] | None = "",
|
hardcore_position_config: str | dict[str, Any] | None = "",
|
||||||
location_config: str | dict[str, Any] | None = "",
|
location_config: str | dict[str, Any] | None = "",
|
||||||
composition_config: str | dict[str, Any] | None = "",
|
composition_config: str | dict[str, Any] | None = "",
|
||||||
|
style_config: str | dict[str, Any] | None = "",
|
||||||
extra_positive: str = "",
|
extra_positive: str = "",
|
||||||
extra_negative: str = "",
|
extra_negative: str = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
@@ -2624,6 +2664,7 @@ def build_prompt_from_configs(
|
|||||||
hardcore_position_config=hardcore_position_config,
|
hardcore_position_config=hardcore_position_config,
|
||||||
location_config=location_config,
|
location_config=location_config,
|
||||||
composition_config=composition_config,
|
composition_config=composition_config,
|
||||||
|
style_config=style_config,
|
||||||
extra_positive=extra_positive,
|
extra_positive=extra_positive,
|
||||||
extra_negative=extra_negative,
|
extra_negative=extra_negative,
|
||||||
),
|
),
|
||||||
@@ -2801,6 +2842,7 @@ def build_insta_of_pair(
|
|||||||
hardcore_position_config: str | dict[str, Any] | None = "",
|
hardcore_position_config: str | dict[str, Any] | None = "",
|
||||||
location_config: str | dict[str, Any] | None = "",
|
location_config: str | dict[str, Any] | None = "",
|
||||||
composition_config: str | dict[str, Any] | None = "",
|
composition_config: str | dict[str, Any] | None = "",
|
||||||
|
style_config: str | dict[str, Any] | None = "",
|
||||||
extra_positive: str = "",
|
extra_positive: str = "",
|
||||||
extra_negative: str = "",
|
extra_negative: str = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
@@ -2825,6 +2867,7 @@ def build_insta_of_pair(
|
|||||||
hardcore_position_config=hardcore_position_config,
|
hardcore_position_config=hardcore_position_config,
|
||||||
location_config=location_config,
|
location_config=location_config,
|
||||||
composition_config=composition_config,
|
composition_config=composition_config,
|
||||||
|
style_config=style_config,
|
||||||
extra_positive=extra_positive,
|
extra_positive=extra_positive,
|
||||||
extra_negative=extra_negative,
|
extra_negative=extra_negative,
|
||||||
)
|
)
|
||||||
|
|||||||
+19
-12
@@ -8,18 +8,20 @@ try:
|
|||||||
from . import category_library as category_policy
|
from . import category_library as category_policy
|
||||||
from . import generate_prompt_batches as g
|
from . import generate_prompt_batches as g
|
||||||
from . import row_camera as row_camera_policy
|
from . import row_camera as row_camera_policy
|
||||||
|
from . import style_config as style_config_policy
|
||||||
except ImportError: # Allows local smoke tests from the repository root.
|
except ImportError: # Allows local smoke tests from the repository root.
|
||||||
import category_library as category_policy
|
import category_library as category_policy
|
||||||
import generate_prompt_batches as g
|
import generate_prompt_batches as g
|
||||||
import row_camera as row_camera_policy
|
import row_camera as row_camera_policy
|
||||||
|
import style_config as style_config_policy
|
||||||
|
|
||||||
|
|
||||||
GENERIC_POSITIVE_SUFFIX = (
|
GENERIC_POSITIVE_SUFFIX = (
|
||||||
"Use crisp clean comic linework, detailed hatching, soft blended shading, "
|
"Use coherent anatomy, readable body placement, natural light response, "
|
||||||
"pastel skin tones, muted blues and pinks, warm sensual lighting, and tactile textured paper."
|
"clear material texture, stable spatial depth, and polished visual detail."
|
||||||
)
|
)
|
||||||
|
|
||||||
DEFAULT_STYLE = "sexy but tasteful adult pin-up coloured-pencil comic illustration"
|
DEFAULT_STYLE = "realistic adult scene with natural camera realism"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -55,7 +57,7 @@ LAYOUT_TEMPLATE = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
DEFAULT_CAPTION_TEMPLATE = (
|
DEFAULT_CAPTION_TEMPLATE = (
|
||||||
"{trigger}, {subject_phrase}, {age}, {item}, {scene}, {composition}, coloured pencil comic illustration"
|
"{trigger}, {subject_phrase}, {age}, {item}, {scene}, {composition}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -72,15 +74,20 @@ def format_template(template: str, context: dict[str, Any]) -> str:
|
|||||||
return template.format_map(safe_context)
|
return template.format_map(safe_context)
|
||||||
|
|
||||||
|
|
||||||
def resolve_row_text_fields(category: dict[str, Any], subcategory: dict[str, Any], item: Any) -> RowTextFields:
|
def resolve_row_text_fields(
|
||||||
|
category: dict[str, Any],
|
||||||
|
subcategory: dict[str, Any],
|
||||||
|
item: Any,
|
||||||
|
style_config: str | dict[str, Any] | None = None,
|
||||||
|
) -> RowTextFields:
|
||||||
|
base_negative = str(category_policy.merged_field(category, subcategory, item, "negative_prompt", g.NEGATIVE_PROMPT))
|
||||||
|
base_suffix = str(category_policy.merged_field(category, subcategory, item, "positive_suffix", GENERIC_POSITIVE_SUFFIX))
|
||||||
|
base_style = str(category_policy.merged_field(category, subcategory, item, "style", DEFAULT_STYLE))
|
||||||
|
style, positive_suffix = style_config_policy.resolve_style_fields(base_style, base_suffix, style_config)
|
||||||
return RowTextFields(
|
return RowTextFields(
|
||||||
negative_prompt=str(
|
negative_prompt=style_config_policy.merge_negative_prompt(base_negative, style_config),
|
||||||
category_policy.merged_field(category, subcategory, item, "negative_prompt", g.NEGATIVE_PROMPT)
|
positive_suffix=positive_suffix,
|
||||||
),
|
style=style,
|
||||||
positive_suffix=str(
|
|
||||||
category_policy.merged_field(category, subcategory, item, "positive_suffix", GENERIC_POSITIVE_SUFFIX)
|
|
||||||
),
|
|
||||||
style=str(category_policy.merged_field(category, subcategory, item, "style", DEFAULT_STYLE)),
|
|
||||||
item_label=str(category_policy.merged_field(category, subcategory, item, "item_label", category["name"])),
|
item_label=str(category_policy.merged_field(category, subcategory, item, "item_label", category["name"])),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
STYLE_CONFIG_SCHEMA = "sxcp_style_config_v1"
|
||||||
|
STYLE_COMBINE_MODES = ("replace", "add", "disabled")
|
||||||
|
STYLE_PRESETS = {
|
||||||
|
"category_default": {
|
||||||
|
"style": "",
|
||||||
|
"positive_suffix": "",
|
||||||
|
"summary": "category default style",
|
||||||
|
},
|
||||||
|
"realistic_photo": {
|
||||||
|
"style": "realistic adult photographic scene, natural camera capture",
|
||||||
|
"positive_suffix": "Use realistic skin texture, natural light response, coherent anatomy, readable contact points, and believable spatial depth.",
|
||||||
|
"summary": "realistic photographic style",
|
||||||
|
},
|
||||||
|
"creator_phone_photo": {
|
||||||
|
"style": "realistic creator-shot phone photo, natural adult social-media image",
|
||||||
|
"positive_suffix": "Use handheld camera realism, natural skin texture, readable body positioning, and believable room depth.",
|
||||||
|
"summary": "creator phone photo style",
|
||||||
|
},
|
||||||
|
"documentary_flash": {
|
||||||
|
"style": "realistic direct-flash documentary photo, raw adult snapshot",
|
||||||
|
"positive_suffix": "Use direct flash, natural skin texture, sharp foreground detail, visible contact points, and unpolished camera realism.",
|
||||||
|
"summary": "direct flash documentary style",
|
||||||
|
},
|
||||||
|
"cinematic_realism": {
|
||||||
|
"style": "cinematic realistic adult scene, natural lens perspective",
|
||||||
|
"positive_suffix": "Use realistic anatomy, readable blocking, natural depth, motivated lighting, and coherent camera perspective.",
|
||||||
|
"summary": "cinematic realism style",
|
||||||
|
},
|
||||||
|
"comic_pinup_colored_pencil": {
|
||||||
|
"style": "adult erotic coloured-pencil comic pin-up style",
|
||||||
|
"positive_suffix": "Use crisp comic linework, detailed hatching, warm erotic lighting, soft skin shading, and tactile textured paper.",
|
||||||
|
"summary": "colored-pencil comic pin-up style",
|
||||||
|
},
|
||||||
|
"flat_vector_comic": {
|
||||||
|
"style": "flat vector adult comic illustration",
|
||||||
|
"positive_suffix": "Use flat color, clean graphic shapes, crisp outlines, simplified shadows, and readable adult body positioning.",
|
||||||
|
"summary": "flat vector comic style",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def style_pool_preset_choices() -> list[str]:
|
||||||
|
return list(STYLE_PRESETS)
|
||||||
|
|
||||||
|
|
||||||
|
def style_combine_mode_choices() -> list[str]:
|
||||||
|
return list(STYLE_COMBINE_MODES)
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_text(value: Any) -> str:
|
||||||
|
return str(value or "").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _join_text(*values: Any) -> str:
|
||||||
|
parts: list[str] = []
|
||||||
|
for value in values:
|
||||||
|
text = _clean_text(value)
|
||||||
|
if text and text not in parts:
|
||||||
|
parts.append(text.rstrip("."))
|
||||||
|
return ". ".join(parts)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_style_config(style_config: str | dict[str, Any] | None) -> dict[str, Any]:
|
||||||
|
if not style_config:
|
||||||
|
return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
|
||||||
|
if isinstance(style_config, dict):
|
||||||
|
raw = dict(style_config)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
raw = json.loads(str(style_config))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
|
||||||
|
if raw.get("schema") != STYLE_CONFIG_SCHEMA:
|
||||||
|
return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
|
||||||
|
combine_mode = _clean_text(raw.get("combine_mode")) or "replace"
|
||||||
|
if combine_mode not in STYLE_COMBINE_MODES:
|
||||||
|
combine_mode = "replace"
|
||||||
|
return {
|
||||||
|
"schema": STYLE_CONFIG_SCHEMA,
|
||||||
|
"version": 1,
|
||||||
|
"enabled": bool(raw.get("enabled", True)) and combine_mode != "disabled",
|
||||||
|
"combine_mode": combine_mode,
|
||||||
|
"preset": _clean_text(raw.get("preset")) or "category_default",
|
||||||
|
"style": _clean_text(raw.get("style")),
|
||||||
|
"positive_suffix": _clean_text(raw.get("positive_suffix")),
|
||||||
|
"negative_prompt": _clean_text(raw.get("negative_prompt")),
|
||||||
|
"summary": _clean_text(raw.get("summary")) or "style config",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_style_config_json(
|
||||||
|
*,
|
||||||
|
enabled: bool = True,
|
||||||
|
combine_mode: str = "replace",
|
||||||
|
preset: str = "category_default",
|
||||||
|
custom_style: str = "",
|
||||||
|
custom_positive_suffix: str = "",
|
||||||
|
custom_negative: str = "",
|
||||||
|
style_config: str | dict[str, Any] | None = "",
|
||||||
|
) -> str:
|
||||||
|
if combine_mode not in STYLE_COMBINE_MODES:
|
||||||
|
combine_mode = "replace"
|
||||||
|
base = parse_style_config(style_config)
|
||||||
|
preset_entry = STYLE_PRESETS.get(preset, STYLE_PRESETS["category_default"])
|
||||||
|
style = _clean_text(custom_style) or preset_entry["style"]
|
||||||
|
positive_suffix = _clean_text(custom_positive_suffix) or preset_entry["positive_suffix"]
|
||||||
|
negative_prompt = _clean_text(custom_negative)
|
||||||
|
if combine_mode == "add" and base.get("enabled"):
|
||||||
|
style = _join_text(base.get("style"), style)
|
||||||
|
positive_suffix = _join_text(base.get("positive_suffix"), positive_suffix)
|
||||||
|
negative_prompt = _join_text(base.get("negative_prompt"), negative_prompt)
|
||||||
|
payload = {
|
||||||
|
"schema": STYLE_CONFIG_SCHEMA,
|
||||||
|
"version": 1,
|
||||||
|
"enabled": bool(enabled) and combine_mode != "disabled",
|
||||||
|
"combine_mode": combine_mode,
|
||||||
|
"preset": preset,
|
||||||
|
"style": style,
|
||||||
|
"positive_suffix": positive_suffix,
|
||||||
|
"negative_prompt": negative_prompt,
|
||||||
|
"summary": "style disabled" if not enabled or combine_mode == "disabled" else preset_entry["summary"],
|
||||||
|
}
|
||||||
|
return json.dumps(payload, ensure_ascii=True, sort_keys=True)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_style_fields(base_style: str, base_positive_suffix: str, style_config: str | dict[str, Any] | None) -> tuple[str, str]:
|
||||||
|
config = parse_style_config(style_config)
|
||||||
|
if not config.get("enabled"):
|
||||||
|
return base_style, base_positive_suffix
|
||||||
|
if config["combine_mode"] == "add":
|
||||||
|
return (
|
||||||
|
_join_text(base_style, config.get("style")),
|
||||||
|
_join_text(base_positive_suffix, config.get("positive_suffix")),
|
||||||
|
)
|
||||||
|
return config.get("style", "") or base_style, config.get("positive_suffix", "") or base_positive_suffix
|
||||||
|
|
||||||
|
|
||||||
|
def merge_negative_prompt(base_negative: str, style_config: str | dict[str, Any] | None) -> str:
|
||||||
|
config = parse_style_config(style_config)
|
||||||
|
if not config.get("enabled"):
|
||||||
|
return base_negative
|
||||||
|
return _join_text(base_negative, config.get("negative_prompt"))
|
||||||
@@ -477,6 +477,7 @@ def _prompt_row(
|
|||||||
camera_config: str | dict[str, Any] | None = "",
|
camera_config: str | dict[str, Any] | None = "",
|
||||||
location_config: str | dict[str, Any] | None = "",
|
location_config: str | dict[str, Any] | None = "",
|
||||||
composition_config: str | dict[str, Any] | None = "",
|
composition_config: str | dict[str, Any] | None = "",
|
||||||
|
style_config: str | dict[str, Any] | None = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
row = pb.build_prompt(
|
row = pb.build_prompt(
|
||||||
category=category,
|
category=category,
|
||||||
@@ -507,6 +508,7 @@ def _prompt_row(
|
|||||||
camera_config=camera_config,
|
camera_config=camera_config,
|
||||||
location_config=location_config,
|
location_config=location_config,
|
||||||
composition_config=composition_config,
|
composition_config=composition_config,
|
||||||
|
style_config=style_config,
|
||||||
)
|
)
|
||||||
_expect_row_base(row, name)
|
_expect_row_base(row, name)
|
||||||
return row
|
return row
|
||||||
@@ -3385,6 +3387,16 @@ def smoke_row_rendering_policy() -> None:
|
|||||||
_expect(default_text_fields.positive_suffix == row_rendering.GENERIC_POSITIVE_SUFFIX, "Row text fields lost suffix default")
|
_expect(default_text_fields.positive_suffix == row_rendering.GENERIC_POSITIVE_SUFFIX, "Row text fields lost suffix default")
|
||||||
_expect(default_text_fields.style == row_rendering.DEFAULT_STYLE, "Row text fields lost style default")
|
_expect(default_text_fields.style == row_rendering.DEFAULT_STYLE, "Row text fields lost style default")
|
||||||
_expect(default_text_fields.item_label == "Default Category", "Row text fields lost category-name label default")
|
_expect(default_text_fields.item_label == "Default Category", "Row text fields lost category-name label default")
|
||||||
|
style_config = pb.build_style_config_json(preset="comic_pinup_colored_pencil")
|
||||||
|
styled_fields = row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text, style_config)
|
||||||
|
_expect("comic pin-up" in styled_fields.style, "Style Pool did not override row style")
|
||||||
|
_expect("comic linework" in styled_fields.positive_suffix, "Style Pool did not override row style suffix")
|
||||||
|
negative_style_config = pb.build_style_config_json(
|
||||||
|
preset="realistic_photo",
|
||||||
|
custom_negative="flat vector, comic paper texture",
|
||||||
|
)
|
||||||
|
negative_fields = row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text, negative_style_config)
|
||||||
|
_expect("comic paper texture" in negative_fields.negative_prompt, "Style Pool did not merge style negatives")
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"trigger": Trigger,
|
"trigger": Trigger,
|
||||||
@@ -5713,6 +5725,20 @@ def smoke_hardcore_category_routes() -> None:
|
|||||||
_expect(sdxl_tag in (sdxl.get("sdxl_prompt") or "").lower(), f"{name} SDXL prompt did not include family tag {sdxl_tag!r}")
|
_expect(sdxl_tag in (sdxl.get("sdxl_prompt") or "").lower(), f"{name} SDXL prompt did not include family tag {sdxl_tag!r}")
|
||||||
caption, _method = caption_naturalizer.naturalize_caption("", metadata_json=_json(row), trigger=Trigger, include_trigger=True)
|
caption, _method = caption_naturalizer.naturalize_caption("", metadata_json=_json(row), trigger=Trigger, include_trigger=True)
|
||||||
_expect(caption_label in caption.lower(), f"{name} caption did not include family label {caption_label!r}")
|
_expect(caption_label in caption.lower(), f"{name} caption did not include family label {caption_label!r}")
|
||||||
|
styled_row = _prompt_row(
|
||||||
|
name="hardcore_style_pool_override",
|
||||||
|
category="Hardcore sexual poses",
|
||||||
|
subcategory="Penetrative sex",
|
||||||
|
seed=1181,
|
||||||
|
character_cast=cast,
|
||||||
|
women_count=1,
|
||||||
|
men_count=1,
|
||||||
|
hardcore_position_config=_action_filter("penetration_only"),
|
||||||
|
style_config=pb.build_style_config_json(preset="comic_pinup_colored_pencil"),
|
||||||
|
)
|
||||||
|
_expect("comic pin-up" in styled_row.get("style", ""), "Style Pool did not reach generated hardcore row style")
|
||||||
|
_expect("comic linework" in styled_row.get("positive_suffix", ""), "Style Pool did not reach generated hardcore row suffix")
|
||||||
|
_expect("comic pin-up" in styled_row.get("prompt", ""), "Style Pool style was not rendered into hardcore prompt")
|
||||||
multi_cases = [
|
multi_cases = [
|
||||||
("hardcore_threesome", "Threesomes", "threesome_only", "threesome", {"threesome", "toy_double"}, "threesome", "three-person action", 1, 2),
|
("hardcore_threesome", "Threesomes", "threesome_only", "threesome", {"threesome", "toy_double"}, "threesome", "three-person action", 1, 2),
|
||||||
("hardcore_group", "Group sex and orgy", "group_only", "group", {"group", "toy_double"}, "group sex", "group action", 2, 2),
|
("hardcore_group", "Group sex and orgy", "group_only", "group", {"group", "toy_double"}, "group sex", "group action", 2, 2),
|
||||||
@@ -8239,6 +8265,7 @@ def smoke_node_route_config_registration() -> None:
|
|||||||
"SxCPLocationPool",
|
"SxCPLocationPool",
|
||||||
"SxCPCompositionPool",
|
"SxCPCompositionPool",
|
||||||
"SxCPLocationTheme",
|
"SxCPLocationTheme",
|
||||||
|
"SxCPStylePool",
|
||||||
"SxCPCastControl",
|
"SxCPCastControl",
|
||||||
"SxCPCastBias",
|
"SxCPCastBias",
|
||||||
]
|
]
|
||||||
@@ -8286,6 +8313,23 @@ def smoke_node_route_config_registration() -> None:
|
|||||||
_expect(json.loads(theme_composition).get("composition_entries"), "Location Theme did not output compositions")
|
_expect(json.loads(theme_composition).get("composition_entries"), "Location Theme did not output compositions")
|
||||||
_expect("semi_public_affair" in theme_summary, "Location Theme summary lost theme name")
|
_expect("semi_public_affair" in theme_summary, "Location Theme summary lost theme name")
|
||||||
|
|
||||||
|
style_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPStylePool"]
|
||||||
|
style_inputs = style_node.INPUT_TYPES().get("required") or {}
|
||||||
|
_expect("preset" in style_inputs, "Style Pool lost preset input")
|
||||||
|
_expect("tooltip" in style_inputs["preset"][1], "Style Pool tooltip injection missing")
|
||||||
|
style_config, style_summary = style_node().build(
|
||||||
|
True,
|
||||||
|
"replace",
|
||||||
|
"comic_pinup_colored_pencil",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
parsed_style = json.loads(style_config)
|
||||||
|
_expect(parsed_style.get("schema") == "sxcp_style_config_v1", "Style Pool emitted wrong schema")
|
||||||
|
_expect("comic pin-up" in parsed_style.get("style", ""), "Style Pool lost comic preset style")
|
||||||
|
_expect("comic pin-up" in style_summary, "Style Pool summary lost preset label")
|
||||||
|
|
||||||
cast_config, women_count, men_count, cast_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCastControl"]().build(
|
cast_config, women_count, men_count, cast_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCastControl"]().build(
|
||||||
"mixed_couple",
|
"mixed_couple",
|
||||||
1,
|
1,
|
||||||
|
|||||||
Reference in New Issue
Block a user