Add caption pair target routing
This commit is contained in:
@@ -448,10 +448,14 @@ single combined natural caption with the shared descriptor plus separate
|
|||||||
softcore and hardcore version descriptions. It uses the final selected
|
softcore and hardcore version descriptions. It uses the final selected
|
||||||
expression and composition from the generated rows, including any expression
|
expression and composition from the generated rows, including any expression
|
||||||
pool and intensity settings.
|
pool and intensity settings.
|
||||||
|
Set `target=softcore` or `target=hardcore` to emit only one side of the pair for
|
||||||
|
training captions or formatter chains.
|
||||||
|
|
||||||
Naturalizer controls:
|
Naturalizer controls:
|
||||||
|
|
||||||
- `input_hint`: `auto`, `metadata_json`, or `caption_or_prompt`.
|
- `input_hint`: `auto`, `metadata_json`, or `caption_or_prompt`.
|
||||||
|
- `target`: `auto` keeps the combined pair caption; `single`, `softcore`, and
|
||||||
|
`hardcore` mirror the formatter target controls.
|
||||||
- `caption_profile`: `manual_controls` keeps the detail/style/trigger widgets
|
- `caption_profile`: `manual_controls` keeps the detail/style/trigger widgets
|
||||||
authoritative; `training_concise`, `training_dense`, and `browsing` apply
|
authoritative; `training_concise`, `training_dense`, and `browsing` apply
|
||||||
preset caption behavior.
|
preset caption behavior.
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class CaptionFormatRequest:
|
|||||||
source_text: str
|
source_text: str
|
||||||
metadata_json: str = ""
|
metadata_json: str = ""
|
||||||
input_hint: str = "auto"
|
input_hint: str = "auto"
|
||||||
|
target: str = "auto"
|
||||||
trigger: str = ""
|
trigger: str = ""
|
||||||
include_trigger: bool = True
|
include_trigger: bool = True
|
||||||
detail_level: str = "balanced"
|
detail_level: str = "balanced"
|
||||||
@@ -22,6 +23,7 @@ class CaptionFormatRoute:
|
|||||||
method: str
|
method: str
|
||||||
branch: str
|
branch: str
|
||||||
input_hint: str
|
input_hint: str
|
||||||
|
target: str
|
||||||
detail_level: str
|
detail_level: str
|
||||||
style_policy: str
|
style_policy: str
|
||||||
include_trigger: bool
|
include_trigger: bool
|
||||||
@@ -36,7 +38,7 @@ class CaptionFormatDependencies:
|
|||||||
apply_caption_profile: Callable[[str, str, str, bool], tuple[str, str, bool]]
|
apply_caption_profile: Callable[[str, str, str, bool], tuple[str, str, bool]]
|
||||||
keep_style_terms: Callable[[str], bool]
|
keep_style_terms: Callable[[str], bool]
|
||||||
row_from_inputs: Callable[[str, str, str], tuple[dict[str, Any] | None, str]]
|
row_from_inputs: Callable[[str, str, str], tuple[dict[str, Any] | None, str]]
|
||||||
metadata_to_prose: Callable[[dict[str, Any], str, bool], tuple[str, str]]
|
metadata_to_prose: Callable[..., tuple[str, str]]
|
||||||
text_to_prose: Callable[[str, str, bool], tuple[str, str]]
|
text_to_prose: Callable[[str, str, bool], tuple[str, str]]
|
||||||
with_trigger: Callable[[str, str, bool], str]
|
with_trigger: Callable[[str, str, bool], str]
|
||||||
sanitize_prose_text: Callable[..., str]
|
sanitize_prose_text: Callable[..., str]
|
||||||
@@ -47,6 +49,7 @@ def naturalize_caption_result(
|
|||||||
deps: CaptionFormatDependencies,
|
deps: CaptionFormatDependencies,
|
||||||
) -> CaptionFormatRoute:
|
) -> CaptionFormatRoute:
|
||||||
input_hint = request.input_hint if request.input_hint in ("auto", "metadata_json", "caption_or_prompt") else "auto"
|
input_hint = request.input_hint if request.input_hint in ("auto", "metadata_json", "caption_or_prompt") else "auto"
|
||||||
|
target = request.target if request.target in ("auto", "single", "softcore", "hardcore") else "auto"
|
||||||
detail_level, style_policy, include_trigger = deps.apply_caption_profile(
|
detail_level, style_policy, include_trigger = deps.apply_caption_profile(
|
||||||
request.caption_profile,
|
request.caption_profile,
|
||||||
request.detail_level,
|
request.detail_level,
|
||||||
@@ -56,7 +59,7 @@ def naturalize_caption_result(
|
|||||||
keep_style = deps.keep_style_terms(style_policy)
|
keep_style = deps.keep_style_terms(style_policy)
|
||||||
row, row_method = deps.row_from_inputs(request.source_text, request.metadata_json, input_hint)
|
row, row_method = deps.row_from_inputs(request.source_text, request.metadata_json, input_hint)
|
||||||
if row is not None:
|
if row is not None:
|
||||||
prose, method = deps.metadata_to_prose(row, detail_level, keep_style)
|
prose, method = deps.metadata_to_prose(row, detail_level, keep_style, target)
|
||||||
caption = deps.sanitize_prose_text(
|
caption = deps.sanitize_prose_text(
|
||||||
deps.with_trigger(prose, request.trigger, include_trigger),
|
deps.with_trigger(prose, request.trigger, include_trigger),
|
||||||
triggers=(request.trigger,),
|
triggers=(request.trigger,),
|
||||||
@@ -67,6 +70,7 @@ def naturalize_caption_result(
|
|||||||
method=full_method,
|
method=full_method,
|
||||||
branch="metadata",
|
branch="metadata",
|
||||||
input_hint=input_hint,
|
input_hint=input_hint,
|
||||||
|
target=target,
|
||||||
detail_level=detail_level,
|
detail_level=detail_level,
|
||||||
style_policy=style_policy,
|
style_policy=style_policy,
|
||||||
include_trigger=include_trigger,
|
include_trigger=include_trigger,
|
||||||
@@ -83,6 +87,7 @@ def naturalize_caption_result(
|
|||||||
method=method,
|
method=method,
|
||||||
branch="text",
|
branch="text",
|
||||||
input_hint=input_hint,
|
input_hint=input_hint,
|
||||||
|
target=target,
|
||||||
detail_level=detail_level,
|
detail_level=detail_level,
|
||||||
style_policy=style_policy,
|
style_policy=style_policy,
|
||||||
include_trigger=include_trigger,
|
include_trigger=include_trigger,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class CaptionMetadataRouteRequest:
|
|||||||
row: dict[str, Any]
|
row: dict[str, Any]
|
||||||
detail_level: str
|
detail_level: str
|
||||||
keep_style: bool
|
keep_style: bool
|
||||||
|
target: str = "auto"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -46,7 +47,7 @@ class CaptionMetadataRouteDependencies:
|
|||||||
natural_cast_descriptor_text: Callable[[str], str]
|
natural_cast_descriptor_text: Callable[[str], str]
|
||||||
cast_labels: Callable[[str], list[str]]
|
cast_labels: Callable[[str], list[str]]
|
||||||
natural_label_text: Callable[[Any, list[str]], str]
|
natural_label_text: Callable[[Any, list[str]], str]
|
||||||
metadata_to_prose: Callable[[dict[str, Any], str, bool], tuple[str, str]]
|
metadata_to_prose: Callable[..., tuple[str, str]]
|
||||||
|
|
||||||
|
|
||||||
def pronoun(subject: str) -> str:
|
def pronoun(subject: str) -> str:
|
||||||
@@ -300,6 +301,7 @@ def insta_of_pair_from_row_result(
|
|||||||
row = request.row
|
row = request.row
|
||||||
detail_level = request.detail_level
|
detail_level = request.detail_level
|
||||||
keep_style = request.keep_style
|
keep_style = request.keep_style
|
||||||
|
target = request.target if request.target in ("softcore", "hardcore") else "auto"
|
||||||
if deps.clean_text(row.get("mode")).lower() != "insta/of":
|
if deps.clean_text(row.get("mode")).lower() != "insta/of":
|
||||||
return None
|
return None
|
||||||
soft_row = row.get("softcore_row")
|
soft_row = row.get("softcore_row")
|
||||||
@@ -315,8 +317,14 @@ def insta_of_pair_from_row_result(
|
|||||||
if soft_row.get("composition"):
|
if soft_row.get("composition"):
|
||||||
hard_row_for_text["composition"] = soft_row["composition"]
|
hard_row_for_text["composition"] = soft_row["composition"]
|
||||||
|
|
||||||
soft_text, _soft_method = deps.metadata_to_prose(soft_row, detail_level, keep_style)
|
include_soft = target in ("auto", "softcore")
|
||||||
hard_text, _hard_method = deps.metadata_to_prose(hard_row_for_text, detail_level, keep_style)
|
include_hard = target in ("auto", "hardcore")
|
||||||
|
soft_text = ""
|
||||||
|
hard_text = ""
|
||||||
|
if include_soft:
|
||||||
|
soft_text, _soft_method = deps.metadata_to_prose(soft_row, detail_level, keep_style, "single")
|
||||||
|
if include_hard:
|
||||||
|
hard_text, _hard_method = deps.metadata_to_prose(hard_row_for_text, detail_level, keep_style, "single")
|
||||||
descriptor = deps.clean_text(row.get("shared_descriptor"))
|
descriptor = deps.clean_text(row.get("shared_descriptor"))
|
||||||
options = row.get("options") if isinstance(row.get("options"), dict) else {}
|
options = row.get("options") if isinstance(row.get("options"), dict) else {}
|
||||||
cast_descriptors = row.get("shared_cast_descriptors")
|
cast_descriptors = row.get("shared_cast_descriptors")
|
||||||
@@ -335,8 +343,11 @@ def insta_of_pair_from_row_result(
|
|||||||
parts.append(f"A {descriptor}")
|
parts.append(f"A {descriptor}")
|
||||||
if cast_descriptor_text and not same_soft_cast:
|
if cast_descriptor_text and not same_soft_cast:
|
||||||
parts.append(deps.natural_cast_descriptor_text(cast_descriptor_text))
|
parts.append(deps.natural_cast_descriptor_text(cast_descriptor_text))
|
||||||
if same_soft_cast:
|
if same_soft_cast and include_soft:
|
||||||
|
if target == "auto":
|
||||||
parts.append("The softcore version keeps the same adult cast present together in a non-explicit teaser setup")
|
parts.append("The softcore version keeps the same adult cast present together in a non-explicit teaser setup")
|
||||||
|
else:
|
||||||
|
parts.append("The same adult cast is present together in a non-explicit teaser setup")
|
||||||
partner_styling = row.get("softcore_partner_styling")
|
partner_styling = row.get("softcore_partner_styling")
|
||||||
if isinstance(partner_styling, dict):
|
if isinstance(partner_styling, dict):
|
||||||
outfits = partner_styling.get("outfits")
|
outfits = partner_styling.get("outfits")
|
||||||
@@ -349,9 +360,9 @@ def insta_of_pair_from_row_result(
|
|||||||
if pose:
|
if pose:
|
||||||
parts.append(f"The shared softcore cast pose is {pose}")
|
parts.append(f"The shared softcore cast pose is {pose}")
|
||||||
if soft_text:
|
if soft_text:
|
||||||
parts.append(f"Softcore version: {soft_text}")
|
parts.append(f"Softcore version: {soft_text}" if target == "auto" else soft_text)
|
||||||
if hard_text:
|
if hard_text:
|
||||||
parts.append(f"Hardcore version: {hard_text}")
|
parts.append(f"Hardcore version: {hard_text}" if target == "auto" else hard_text)
|
||||||
if not parts:
|
if not parts:
|
||||||
return None
|
return None
|
||||||
return CaptionMetadataRoute(deps.join_sentences(parts), "metadata(insta_of_pair)")
|
return CaptionMetadataRoute(deps.join_sentences(parts), "metadata(insta_of_pair)")
|
||||||
|
|||||||
+46
-12
@@ -172,17 +172,24 @@ def _caption_metadata_route_request(
|
|||||||
row: dict[str, Any],
|
row: dict[str, Any],
|
||||||
detail_level: str,
|
detail_level: str,
|
||||||
keep_style: bool,
|
keep_style: bool,
|
||||||
|
target: str = "auto",
|
||||||
) -> caption_metadata_routes.CaptionMetadataRouteRequest:
|
) -> caption_metadata_routes.CaptionMetadataRouteRequest:
|
||||||
return caption_metadata_routes.CaptionMetadataRouteRequest(
|
return caption_metadata_routes.CaptionMetadataRouteRequest(
|
||||||
row=row,
|
row=row,
|
||||||
detail_level=detail_level,
|
detail_level=detail_level,
|
||||||
keep_style=keep_style,
|
keep_style=keep_style,
|
||||||
|
target=target,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _single_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -> tuple[str, str] | None:
|
def _single_from_row(
|
||||||
|
row: dict[str, Any],
|
||||||
|
detail_level: str,
|
||||||
|
keep_style: bool,
|
||||||
|
target: str = "auto",
|
||||||
|
) -> tuple[str, str] | None:
|
||||||
return caption_metadata_routes.single_from_row(
|
return caption_metadata_routes.single_from_row(
|
||||||
_caption_metadata_route_request(row, detail_level, keep_style),
|
_caption_metadata_route_request(row, detail_level, keep_style, target),
|
||||||
_caption_metadata_route_dependencies(),
|
_caption_metadata_route_dependencies(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -199,35 +206,60 @@ def _couple_clothing_sentence(clothing: str) -> str:
|
|||||||
return caption_metadata_routes.couple_clothing_sentence(clothing, _clean_text)
|
return caption_metadata_routes.couple_clothing_sentence(clothing, _clean_text)
|
||||||
|
|
||||||
|
|
||||||
def _couple_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -> tuple[str, str] | None:
|
def _couple_from_row(
|
||||||
|
row: dict[str, Any],
|
||||||
|
detail_level: str,
|
||||||
|
keep_style: bool,
|
||||||
|
target: str = "auto",
|
||||||
|
) -> tuple[str, str] | None:
|
||||||
return caption_metadata_routes.couple_from_row(
|
return caption_metadata_routes.couple_from_row(
|
||||||
_caption_metadata_route_request(row, detail_level, keep_style),
|
_caption_metadata_route_request(row, detail_level, keep_style, target),
|
||||||
_caption_metadata_route_dependencies(),
|
_caption_metadata_route_dependencies(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _configured_cast_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -> tuple[str, str] | None:
|
def _configured_cast_from_row(
|
||||||
|
row: dict[str, Any],
|
||||||
|
detail_level: str,
|
||||||
|
keep_style: bool,
|
||||||
|
target: str = "auto",
|
||||||
|
) -> tuple[str, str] | None:
|
||||||
return caption_metadata_routes.configured_cast_from_row(
|
return caption_metadata_routes.configured_cast_from_row(
|
||||||
_caption_metadata_route_request(row, detail_level, keep_style),
|
_caption_metadata_route_request(row, detail_level, keep_style, target),
|
||||||
_caption_metadata_route_dependencies(),
|
_caption_metadata_route_dependencies(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _group_or_layout_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -> tuple[str, str] | None:
|
def _group_or_layout_from_row(
|
||||||
|
row: dict[str, Any],
|
||||||
|
detail_level: str,
|
||||||
|
keep_style: bool,
|
||||||
|
target: str = "auto",
|
||||||
|
) -> tuple[str, str] | None:
|
||||||
return caption_metadata_routes.group_or_layout_from_row(
|
return caption_metadata_routes.group_or_layout_from_row(
|
||||||
_caption_metadata_route_request(row, detail_level, keep_style),
|
_caption_metadata_route_request(row, detail_level, keep_style, target),
|
||||||
_caption_metadata_route_dependencies(),
|
_caption_metadata_route_dependencies(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _insta_of_pair_from_row(row: dict[str, Any], detail_level: str, keep_style: bool) -> tuple[str, str] | None:
|
def _insta_of_pair_from_row(
|
||||||
|
row: dict[str, Any],
|
||||||
|
detail_level: str,
|
||||||
|
keep_style: bool,
|
||||||
|
target: str = "auto",
|
||||||
|
) -> tuple[str, str] | None:
|
||||||
return caption_metadata_routes.insta_of_pair_from_row(
|
return caption_metadata_routes.insta_of_pair_from_row(
|
||||||
_caption_metadata_route_request(row, detail_level, keep_style),
|
_caption_metadata_route_request(row, detail_level, keep_style, target),
|
||||||
_caption_metadata_route_dependencies(),
|
_caption_metadata_route_dependencies(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _metadata_to_prose(row: dict[str, Any], detail_level: str, keep_style: bool) -> tuple[str, str]:
|
def _metadata_to_prose(
|
||||||
|
row: dict[str, Any],
|
||||||
|
detail_level: str,
|
||||||
|
keep_style: bool,
|
||||||
|
target: str = "auto",
|
||||||
|
) -> tuple[str, str]:
|
||||||
for builder in (
|
for builder in (
|
||||||
_insta_of_pair_from_row,
|
_insta_of_pair_from_row,
|
||||||
_configured_cast_from_row,
|
_configured_cast_from_row,
|
||||||
@@ -235,7 +267,7 @@ def _metadata_to_prose(row: dict[str, Any], detail_level: str, keep_style: bool)
|
|||||||
_couple_from_row,
|
_couple_from_row,
|
||||||
_group_or_layout_from_row,
|
_group_or_layout_from_row,
|
||||||
):
|
):
|
||||||
result = builder(row, detail_level, keep_style)
|
result = builder(row, detail_level, keep_style, target)
|
||||||
if result:
|
if result:
|
||||||
prose, method = result
|
prose, method = result
|
||||||
return _append_formatter_hints(prose, row), method
|
return _append_formatter_hints(prose, row), method
|
||||||
@@ -346,6 +378,7 @@ def naturalize_caption(
|
|||||||
detail_level: str = "balanced",
|
detail_level: str = "balanced",
|
||||||
style_policy: str = "drop_style_tail",
|
style_policy: str = "drop_style_tail",
|
||||||
caption_profile: str = caption_policy.CAPTION_PROFILE_DEFAULT,
|
caption_profile: str = caption_policy.CAPTION_PROFILE_DEFAULT,
|
||||||
|
target: str = "auto",
|
||||||
) -> tuple[str, str]:
|
) -> tuple[str, str]:
|
||||||
"""Rewrite tag-style prompt/caption text into compact natural language."""
|
"""Rewrite tag-style prompt/caption text into compact natural language."""
|
||||||
return caption_format_route.naturalize_caption(
|
return caption_format_route.naturalize_caption(
|
||||||
@@ -353,6 +386,7 @@ def naturalize_caption(
|
|||||||
source_text=source_text,
|
source_text=source_text,
|
||||||
metadata_json=metadata_json,
|
metadata_json=metadata_json,
|
||||||
input_hint=input_hint,
|
input_hint=input_hint,
|
||||||
|
target=target,
|
||||||
trigger=trigger,
|
trigger=trigger,
|
||||||
include_trigger=include_trigger,
|
include_trigger=include_trigger,
|
||||||
detail_level=detail_level,
|
detail_level=detail_level,
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ def detail_allows(level: str, dense_only: bool = False) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def metadata_route_dependencies(
|
def metadata_route_dependencies(
|
||||||
metadata_to_prose: Callable[[dict[str, Any], str, bool], tuple[str, str]],
|
metadata_to_prose: Callable[..., tuple[str, str]],
|
||||||
) -> caption_metadata_routes.CaptionMetadataRouteDependencies:
|
) -> caption_metadata_routes.CaptionMetadataRouteDependencies:
|
||||||
return caption_metadata_routes.CaptionMetadataRouteDependencies(
|
return caption_metadata_routes.CaptionMetadataRouteDependencies(
|
||||||
item_labels=ITEM_LABELS,
|
item_labels=ITEM_LABELS,
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ Core helper ownership:
|
|||||||
| `sdxl_presets.py` | SDXL formatter profiles, style presets, quality presets, default negative prompt, and metadata-family tag hints used by the SDXL formatter and node choice lists. |
|
| `sdxl_presets.py` | SDXL formatter profiles, style presets, quality presets, default negative prompt, and metadata-family tag hints used by the SDXL formatter and node choice lists. |
|
||||||
| `sdxl_format_route.py` | Top-level SDXL dispatch, formatter profile application, target and nude-weight normalization, metadata-vs-text input selection, single-vs-pair branching, final prompt/negative output shape, and fallback routing. |
|
| `sdxl_format_route.py` | Top-level SDXL dispatch, formatter profile application, target and nude-weight normalization, metadata-vs-text input selection, single-vs-pair branching, final prompt/negative output shape, and fallback routing. |
|
||||||
| `sdxl_tag_policy.py` | SDXL tag splitting, tag-key dedupe, count inference, character descriptor tags, metadata-family/camera/explicit helper tags, and route dependency assembly used by `sdxl_formatter.py` and `sdxl_tag_routes.py`. |
|
| `sdxl_tag_policy.py` | SDXL tag splitting, tag-key dedupe, count inference, character descriptor tags, metadata-family/camera/explicit helper tags, and route dependency assembly used by `sdxl_formatter.py` and `sdxl_tag_routes.py`. |
|
||||||
| `caption_format_route.py` | Top-level caption dispatch, input-hint normalization, caption profile application, metadata-vs-text branching, trigger wrapping, final prose hygiene, and method/output shape. |
|
| `caption_format_route.py` | Top-level caption dispatch, input-hint and target normalization, caption profile application, metadata-vs-text branching, trigger wrapping, final prose hygiene, and method/output shape. |
|
||||||
| `caption_policy.py` | Caption naturalizer policy data and helpers: caption profiles, style tails, item labels, metadata-family caption labels, detail/style-policy normalization, clothing cleanup, and composition cleanup. |
|
| `caption_policy.py` | Caption naturalizer policy data and helpers: caption profiles, style tails, item labels, metadata-family caption labels, detail/style-policy normalization, clothing cleanup, and composition cleanup. |
|
||||||
| `caption_text_policy.py` | Caption sentence helpers, trigger wrapping, formatter-hint append, row-value fallback wrappers, cast text wrappers, single-caption front parsing, and metadata-route dependency assembly used by `caption_naturalizer.py` and `caption_metadata_routes.py`. |
|
| `caption_text_policy.py` | Caption sentence helpers, trigger wrapping, formatter-hint append, row-value fallback wrappers, cast text wrappers, single-caption front parsing, and metadata-route dependency assembly used by `caption_naturalizer.py` and `caption_metadata_routes.py`. |
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ recoverable.
|
|||||||
| `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/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 |
|
| `SxCP Krea2 Formatter` | `source_text`, connectable `metadata_json`, target | `krea_prompt`, both pair prompts if pair metadata exists, negative outputs, method |
|
||||||
| `SxCP SDXL Formatter` | `source_text`, connectable `metadata_json`, target, style/quality preset | `sdxl_prompt`, both pair prompts if pair metadata exists, negative outputs, method |
|
| `SxCP SDXL Formatter` | `source_text`, connectable `metadata_json`, target, style/quality preset | `sdxl_prompt`, both pair prompts if pair metadata exists, negative outputs, method |
|
||||||
| `SxCP Caption Naturalizer` | `source_text`, connectable `metadata_json` | `natural_caption`, method |
|
| `SxCP Caption Naturalizer` | `source_text`, connectable `metadata_json`, target | `natural_caption`, method |
|
||||||
|
|
||||||
## Practical Recipes
|
## Practical Recipes
|
||||||
|
|
||||||
@@ -170,6 +170,7 @@ These recipes identify the intended road before editing prompt text.
|
|||||||
| 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` |
|
||||||
|
| Convert pair metadata to one training caption side | Pair `metadata_json` -> Caption Naturalizer | `target=softcore` or `target=hardcore`; use `training_concise` or `training_dense` as needed | `caption_format_route.py`, `caption_metadata_routes.insta_of_pair_from_row_result` |
|
||||||
| Save/reuse character | Slot/profile nodes -> Profile Save/Load -> slot/builder | Save from the row/profile data you want, not a freshly randomized disconnected route | `character_profile.py`, `web/profile_buttons.js`, profile JSON |
|
| Save/reuse character | Slot/profile nodes -> Profile Save/Load -> slot/builder | Save from the row/profile data you want, not a freshly randomized disconnected route | `character_profile.py`, `web/profile_buttons.js`, profile JSON |
|
||||||
|
|
||||||
## Seed Axes
|
## Seed Axes
|
||||||
@@ -757,7 +758,7 @@ Naturalizer field consumption:
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Normal single/couple/group | subject fields, age/body, item, scene, expression, composition, camera scene | `caption_metadata_routes.single_from_row_result`, `caption_metadata_routes.couple_from_row_result`, `caption_metadata_routes.group_or_layout_from_row_result` |
|
| Normal single/couple/group | subject fields, age/body, item, scene, expression, composition, camera scene | `caption_metadata_routes.single_from_row_result`, `caption_metadata_routes.couple_from_row_result`, `caption_metadata_routes.group_or_layout_from_row_result` |
|
||||||
| Configured cast/hardcore | `cast_descriptor_text`, `action_family`, `position_family`, `role_graph`, `item`, `scene_text`, expression, composition | `caption_metadata_routes.configured_cast_from_row_result`, `caption_text_policy.metadata_action_label` |
|
| Configured cast/hardcore | `cast_descriptor_text`, `action_family`, `position_family`, `role_graph`, `item`, `scene_text`, expression, composition | `caption_metadata_routes.configured_cast_from_row_result`, `caption_text_policy.metadata_action_label` |
|
||||||
| Insta/OF pair | `softcore_row`, `hardcore_row`, pair options and continuity | `caption_metadata_routes.insta_of_pair_from_row_result` |
|
| Insta/OF pair | `softcore_row`, `hardcore_row`, pair options and continuity, target | `caption_metadata_routes.insta_of_pair_from_row_result` |
|
||||||
| Text fallback | `caption` or `prompt` text | `caption_naturalizer._text_to_prose`, with sentence helpers delegated to `caption_text_policy.py` |
|
| Text fallback | `caption` or `prompt` text | `caption_naturalizer._text_to_prose`, with sentence helpers delegated to `caption_text_policy.py` |
|
||||||
|
|
||||||
### Final Text Hygiene
|
### Final Text Hygiene
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class SxCPCaptionNaturalizer:
|
|||||||
"style_policy": (["drop_style_tail", "keep_style_terms"], {"default": "drop_style_tail"}),
|
"style_policy": (["drop_style_tail", "keep_style_terms"], {"default": "drop_style_tail"}),
|
||||||
"trigger": ("STRING", {"default": "sxcppnl7"}),
|
"trigger": ("STRING", {"default": "sxcppnl7"}),
|
||||||
"include_trigger": ("BOOLEAN", {"default": True}),
|
"include_trigger": ("BOOLEAN", {"default": True}),
|
||||||
|
"target": (["auto", "single", "softcore", "hardcore"], {"default": "auto"}),
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"source_text_input": ("STRING", {"forceInput": True}),
|
"source_text_input": ("STRING", {"forceInput": True}),
|
||||||
@@ -55,6 +56,7 @@ class SxCPCaptionNaturalizer:
|
|||||||
style_policy,
|
style_policy,
|
||||||
trigger,
|
trigger,
|
||||||
include_trigger,
|
include_trigger,
|
||||||
|
target="auto",
|
||||||
source_text_input="",
|
source_text_input="",
|
||||||
metadata_json="",
|
metadata_json="",
|
||||||
):
|
):
|
||||||
@@ -63,6 +65,7 @@ class SxCPCaptionNaturalizer:
|
|||||||
source_text=active_source_text,
|
source_text=active_source_text,
|
||||||
metadata_json=metadata_json or "",
|
metadata_json=metadata_json or "",
|
||||||
input_hint=input_hint,
|
input_hint=input_hint,
|
||||||
|
target=target,
|
||||||
trigger=trigger,
|
trigger=trigger,
|
||||||
include_trigger=include_trigger,
|
include_trigger=include_trigger,
|
||||||
detail_level=detail_level,
|
detail_level=detail_level,
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ def _expect_formatter_outputs(row: dict[str, Any], name: str, *, target: str = "
|
|||||||
caption, method = caption_naturalizer.naturalize_caption(
|
caption, method = caption_naturalizer.naturalize_caption(
|
||||||
"",
|
"",
|
||||||
metadata_json=metadata,
|
metadata_json=metadata,
|
||||||
|
target=target,
|
||||||
trigger=Trigger,
|
trigger=Trigger,
|
||||||
include_trigger=True,
|
include_trigger=True,
|
||||||
)
|
)
|
||||||
@@ -2688,6 +2689,7 @@ def smoke_caption_format_route_policy() -> None:
|
|||||||
source_text="",
|
source_text="",
|
||||||
metadata_json=_json(row),
|
metadata_json=_json(row),
|
||||||
input_hint="metadata_json",
|
input_hint="metadata_json",
|
||||||
|
target="single",
|
||||||
trigger=Trigger,
|
trigger=Trigger,
|
||||||
include_trigger=False,
|
include_trigger=False,
|
||||||
detail_level="concise",
|
detail_level="concise",
|
||||||
@@ -2702,6 +2704,7 @@ def smoke_caption_format_route_policy() -> None:
|
|||||||
"",
|
"",
|
||||||
metadata_json=metadata_request.metadata_json,
|
metadata_json=metadata_request.metadata_json,
|
||||||
input_hint=metadata_request.input_hint,
|
input_hint=metadata_request.input_hint,
|
||||||
|
target=metadata_request.target,
|
||||||
trigger=metadata_request.trigger,
|
trigger=metadata_request.trigger,
|
||||||
include_trigger=metadata_request.include_trigger,
|
include_trigger=metadata_request.include_trigger,
|
||||||
detail_level=metadata_request.detail_level,
|
detail_level=metadata_request.detail_level,
|
||||||
@@ -2711,6 +2714,7 @@ def smoke_caption_format_route_policy() -> None:
|
|||||||
_expect(typed_metadata.as_tuple() == public_metadata, "Typed caption format route should match public metadata output")
|
_expect(typed_metadata.as_tuple() == public_metadata, "Typed caption format route should match public metadata output")
|
||||||
_expect(typed_metadata.branch == "metadata", "Typed caption format route changed metadata branch")
|
_expect(typed_metadata.branch == "metadata", "Typed caption format route changed metadata branch")
|
||||||
_expect(typed_metadata.input_hint == "metadata_json", "Typed caption route lost input hint")
|
_expect(typed_metadata.input_hint == "metadata_json", "Typed caption route lost input hint")
|
||||||
|
_expect(typed_metadata.target == "single", "Typed caption route lost target normalization")
|
||||||
_expect(typed_metadata.detail_level == "dense", "Typed caption route lost training_dense detail override")
|
_expect(typed_metadata.detail_level == "dense", "Typed caption route lost training_dense detail override")
|
||||||
_expect(typed_metadata.style_policy == "drop_style_tail", "Typed caption route lost training_dense style override")
|
_expect(typed_metadata.style_policy == "drop_style_tail", "Typed caption route lost training_dense style override")
|
||||||
_expect(typed_metadata.include_trigger is True, "Typed caption route lost training_dense trigger override")
|
_expect(typed_metadata.include_trigger is True, "Typed caption route lost training_dense trigger override")
|
||||||
@@ -2719,6 +2723,7 @@ def smoke_caption_format_route_policy() -> None:
|
|||||||
fallback_request = caption_format_route.CaptionFormatRequest(
|
fallback_request = caption_format_route.CaptionFormatRequest(
|
||||||
source_text="woman, red dress, studio, coloured pencil comic illustration",
|
source_text="woman, red dress, studio, coloured pencil comic illustration",
|
||||||
input_hint="bad_hint",
|
input_hint="bad_hint",
|
||||||
|
target="weird",
|
||||||
trigger=Trigger,
|
trigger=Trigger,
|
||||||
include_trigger=True,
|
include_trigger=True,
|
||||||
detail_level="dense",
|
detail_level="dense",
|
||||||
@@ -2732,6 +2737,7 @@ def smoke_caption_format_route_policy() -> None:
|
|||||||
public_fallback = caption_naturalizer.naturalize_caption(
|
public_fallback = caption_naturalizer.naturalize_caption(
|
||||||
fallback_request.source_text,
|
fallback_request.source_text,
|
||||||
input_hint=fallback_request.input_hint,
|
input_hint=fallback_request.input_hint,
|
||||||
|
target=fallback_request.target,
|
||||||
trigger=fallback_request.trigger,
|
trigger=fallback_request.trigger,
|
||||||
include_trigger=fallback_request.include_trigger,
|
include_trigger=fallback_request.include_trigger,
|
||||||
detail_level=fallback_request.detail_level,
|
detail_level=fallback_request.detail_level,
|
||||||
@@ -2741,6 +2747,7 @@ def smoke_caption_format_route_policy() -> None:
|
|||||||
_expect(typed_fallback.as_tuple() == public_fallback, "Typed caption format route should match public fallback output")
|
_expect(typed_fallback.as_tuple() == public_fallback, "Typed caption format route should match public fallback output")
|
||||||
_expect(typed_fallback.branch == "text", "Typed caption format route changed fallback branch")
|
_expect(typed_fallback.branch == "text", "Typed caption format route changed fallback branch")
|
||||||
_expect(typed_fallback.input_hint == "auto", "Typed caption route should normalize invalid input hint")
|
_expect(typed_fallback.input_hint == "auto", "Typed caption route should normalize invalid input hint")
|
||||||
|
_expect(typed_fallback.target == "auto", "Typed caption route should normalize invalid target")
|
||||||
_expect(typed_fallback.include_trigger is False, "Typed caption browsing profile should disable trigger")
|
_expect(typed_fallback.include_trigger is False, "Typed caption browsing profile should disable trigger")
|
||||||
_expect(typed_fallback.keep_style is True, "Typed caption browsing profile should keep style terms")
|
_expect(typed_fallback.keep_style is True, "Typed caption browsing profile should keep style terms")
|
||||||
_expect(not typed_fallback.caption.startswith(Trigger), "Typed caption fallback route should not prepend browsing trigger")
|
_expect(not typed_fallback.caption.startswith(Trigger), "Typed caption fallback route should not prepend browsing trigger")
|
||||||
@@ -2898,6 +2905,34 @@ def smoke_caption_metadata_routes() -> None:
|
|||||||
caption_naturalizer._insta_of_pair_from_row,
|
caption_naturalizer._insta_of_pair_from_row,
|
||||||
"metadata(insta_of_pair)",
|
"metadata(insta_of_pair)",
|
||||||
)
|
)
|
||||||
|
deps = caption_naturalizer._caption_metadata_route_dependencies()
|
||||||
|
soft_route = caption_metadata_routes.insta_of_pair_from_row_result(
|
||||||
|
caption_naturalizer._caption_metadata_route_request(pair, "balanced", False, target="softcore"),
|
||||||
|
deps,
|
||||||
|
)
|
||||||
|
hard_route = caption_metadata_routes.insta_of_pair_from_row_result(
|
||||||
|
caption_naturalizer._caption_metadata_route_request(pair, "balanced", False, target="hardcore"),
|
||||||
|
deps,
|
||||||
|
)
|
||||||
|
_expect(soft_route is not None, "Caption pair softcore target did not match")
|
||||||
|
_expect(hard_route is not None, "Caption pair hardcore target did not match")
|
||||||
|
assert soft_route is not None
|
||||||
|
assert hard_route is not None
|
||||||
|
_expect("Softcore version:" not in soft_route.prose, "Caption softcore target should not keep combined pair labels")
|
||||||
|
_expect("Hardcore version:" not in soft_route.prose, "Caption softcore target should not include hard label")
|
||||||
|
_expect("Softcore version:" not in hard_route.prose, "Caption hardcore target should not include soft label")
|
||||||
|
_expect("Hardcore version:" not in hard_route.prose, "Caption hardcore target should not keep combined pair labels")
|
||||||
|
_expect(soft_route.prose != hard_route.prose, "Caption pair soft/hard targets should produce distinct prose")
|
||||||
|
public_hard, public_hard_method = caption_naturalizer.naturalize_caption(
|
||||||
|
"",
|
||||||
|
metadata_json=_json(pair),
|
||||||
|
input_hint="metadata_json",
|
||||||
|
target="hardcore",
|
||||||
|
trigger=Trigger,
|
||||||
|
include_trigger=False,
|
||||||
|
)
|
||||||
|
_expect(public_hard == hard_route.prose, "Public caption hardcore target drifted from typed route")
|
||||||
|
_expect("metadata(insta_of_pair)" in public_hard_method, "Public caption hardcore target lost pair method")
|
||||||
|
|
||||||
|
|
||||||
def smoke_sdxl_presets_policy() -> None:
|
def smoke_sdxl_presets_policy() -> None:
|
||||||
@@ -5854,6 +5889,7 @@ def smoke_node_formatter_registration() -> None:
|
|||||||
_expect("text(" in caption_method, "Caption Naturalizer method changed unexpectedly")
|
_expect("text(" in caption_method, "Caption Naturalizer method changed unexpectedly")
|
||||||
caption_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"].INPUT_TYPES().get("required") or {}
|
caption_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"].INPUT_TYPES().get("required") or {}
|
||||||
_expect("caption_profile" in caption_inputs, "Caption Naturalizer lost caption_profile input")
|
_expect("caption_profile" in caption_inputs, "Caption Naturalizer lost caption_profile input")
|
||||||
|
_expect("target" in caption_inputs, "Caption Naturalizer lost target input")
|
||||||
_expect("tooltip" in caption_inputs["caption_profile"][1], "Caption profile tooltip injection missing")
|
_expect("tooltip" in caption_inputs["caption_profile"][1], "Caption profile tooltip injection missing")
|
||||||
|
|
||||||
krea_output = krea_node().build(
|
krea_output = krea_node().build(
|
||||||
@@ -5987,6 +6023,7 @@ def smoke_node_formatter_registration() -> None:
|
|||||||
"",
|
"",
|
||||||
metadata_json=pair_metadata,
|
metadata_json=pair_metadata,
|
||||||
input_hint="metadata_json",
|
input_hint="metadata_json",
|
||||||
|
target="hardcore",
|
||||||
trigger=Trigger,
|
trigger=Trigger,
|
||||||
include_trigger=True,
|
include_trigger=True,
|
||||||
detail_level="balanced",
|
detail_level="balanced",
|
||||||
@@ -6001,6 +6038,7 @@ def smoke_node_formatter_registration() -> None:
|
|||||||
"drop_style_tail",
|
"drop_style_tail",
|
||||||
Trigger,
|
Trigger,
|
||||||
True,
|
True,
|
||||||
|
"hardcore",
|
||||||
metadata_json=pair_metadata,
|
metadata_json=pair_metadata,
|
||||||
)
|
)
|
||||||
_expect(
|
_expect(
|
||||||
|
|||||||
Reference in New Issue
Block a user