Keep SDXL explicit tags metadata driven
This commit is contained in:
@@ -802,9 +802,9 @@ SDXL field consumption:
|
||||
|
||||
| Branch | Reads most from | Key functions |
|
||||
| --- | --- | --- |
|
||||
| Normal metadata | cast descriptors, age/body/skin/hair/eyes, `action_family`, `position_family`, `position_keys`, item, role graph, scene, camera config/directive | `sdxl_tag_routes.row_core_tags_result`, `sdxl_tag_policy.metadata_family_tags`, `sdxl_tag_policy.camera_tags` |
|
||||
| Normal metadata | cast descriptors, age/body/skin/hair/eyes, `action_family`, `position_family`, `position_keys`, item, role graph, clothing/body exposure state, scene, camera config/directive | `sdxl_tag_routes.row_core_tags_result`, `sdxl_tag_policy.metadata_family_tags`, `sdxl_tag_policy.camera_tags` |
|
||||
| Pair softcore | `softcore_row`, pair partner styling, root soft camera config | `sdxl_tag_routes.soft_tags_result` |
|
||||
| Pair hardcore | `hardcore_row`, `action_family`, `position_family`, `position_keys`, `hardcore_clothing_state`, hard camera fields, hard prompt text | `sdxl_tag_routes.hard_tags_result`, `sdxl_tag_policy.metadata_family_tags` |
|
||||
| Pair hardcore | `hardcore_row`, `action_family`, `position_family`, `position_keys`, role graph, item, `hardcore_clothing_state`, expression/composition, hard camera fields | `sdxl_tag_routes.hard_tags_result`, `sdxl_tag_policy.metadata_family_tags` |
|
||||
| Text fallback | `source_text`, preserve-trigger setting, shared field-label stripping | `_fallback_text_to_sdxl` |
|
||||
|
||||
SDXL is the right place for model trigger handling, tag ordering, weight syntax,
|
||||
|
||||
+34
-2
@@ -70,6 +70,30 @@ def _pair_counts(row: dict[str, Any], root: dict[str, Any]) -> tuple[int, int]:
|
||||
return _descriptor_counts(root)
|
||||
|
||||
|
||||
def _row_explicit_signal_text(
|
||||
row: dict[str, Any],
|
||||
*,
|
||||
item: str,
|
||||
pose: str,
|
||||
role_graph: str,
|
||||
expression: str,
|
||||
composition: str,
|
||||
deps: SDXLTagRouteDependencies,
|
||||
) -> str:
|
||||
values = (
|
||||
item,
|
||||
pose,
|
||||
role_graph,
|
||||
deps.clean(row.get("hardcore_clothing_state")),
|
||||
deps.clean(row.get("clothing_state")),
|
||||
deps.clean(row.get("clothing")),
|
||||
deps.clean(row.get("scene_kind")),
|
||||
expression,
|
||||
composition,
|
||||
)
|
||||
return " ".join(deps.clean(value) for value in values if deps.clean(value))
|
||||
|
||||
|
||||
def row_core_tags_result(request: SDXLRowTagRequest, deps: SDXLTagRouteDependencies) -> SDXLTagRoute:
|
||||
row = request.row
|
||||
tags: list[str] = []
|
||||
@@ -110,7 +134,15 @@ def row_core_tags_result(request: SDXLRowTagRequest, deps: SDXLTagRouteDependenc
|
||||
for tag in deps.camera_tags(row):
|
||||
deps.add_one(tags, seen, tag)
|
||||
|
||||
combined = " ".join(deps.clean(value) for value in (item, pose, role_graph, row.get("prompt", "")))
|
||||
combined = _row_explicit_signal_text(
|
||||
row,
|
||||
item=item,
|
||||
pose=pose,
|
||||
role_graph=role_graph,
|
||||
expression=expression,
|
||||
composition=composition,
|
||||
deps=deps,
|
||||
)
|
||||
for tag in deps.explicit_tags(combined, request.nude_weight):
|
||||
deps.add_one(tags, seen, tag)
|
||||
return SDXLTagRoute(tags)
|
||||
@@ -191,7 +223,7 @@ def hard_tags_result(request: SDXLPairTagRequest, deps: SDXLTagRouteDependencies
|
||||
deps.add(tags, seen, value)
|
||||
for tag in deps.camera_tags(row, root.get("hardcore_camera_directive"), root.get("hardcore_camera_config")):
|
||||
deps.add_one(tags, seen, tag)
|
||||
combined = " ".join([hard_role, hard_item, hard_clothing, expression, composition, root.get("hardcore_prompt", "") or ""])
|
||||
combined = " ".join([hard_role, hard_item, hard_clothing, expression, composition])
|
||||
for tag in deps.explicit_tags(combined, request.nude_weight):
|
||||
deps.add_one(tags, seen, tag)
|
||||
return SDXLTagRoute(tags)
|
||||
|
||||
@@ -4435,6 +4435,43 @@ def smoke_sdxl_tag_routes() -> None:
|
||||
"Typed SDXL row tag route should match legacy wrapper output",
|
||||
)
|
||||
_expect("sdxl route tag" in typed_row.as_text(), "Typed SDXL row tag route lost route-specific formatter hint")
|
||||
stale_prompt_row = _fixture_hardcore_row(
|
||||
prompt="stale raw prompt mentions fully nude naked pussy penis oral anal semen penetration",
|
||||
item="standing portrait setup",
|
||||
pose="standing pose",
|
||||
role_graph="",
|
||||
source_role_graph="",
|
||||
expression="neutral expression",
|
||||
action_family="",
|
||||
position_family="",
|
||||
position_key="",
|
||||
position_keys=[],
|
||||
)
|
||||
stale_tags = sdxl_tag_routes.row_core_tags_result(
|
||||
sdxl_tag_routes.SDXLRowTagRequest(stale_prompt_row, 1.29),
|
||||
deps,
|
||||
).as_text()
|
||||
for forbidden in ("(naked:", "pussy", "penis", "oral sex", "anal sex", "semen", "penetration"):
|
||||
_expect(forbidden not in stale_tags, f"SDXL row tags should not infer explicit tag from stale prompt: {forbidden}")
|
||||
|
||||
metadata_explicit_row = _fixture_hardcore_row(
|
||||
prompt="stale raw prompt without explicit tag anchors",
|
||||
item="kneeling pose with visible penis and pussy contact",
|
||||
pose="penetration pose",
|
||||
role_graph="penis thrusts into pussy",
|
||||
source_role_graph="penis thrusts into pussy",
|
||||
hardcore_clothing_state="fully nude body, bare skin unobstructed",
|
||||
action_family="penetration",
|
||||
position_family="penetrative",
|
||||
position_key="kneeling",
|
||||
position_keys=["kneeling"],
|
||||
)
|
||||
metadata_tags = sdxl_tag_routes.row_core_tags_result(
|
||||
sdxl_tag_routes.SDXLRowTagRequest(metadata_explicit_row, 1.29),
|
||||
deps,
|
||||
).as_text()
|
||||
for required in ("(naked:1.29)", "pussy", "penis", "penetration"):
|
||||
_expect(required in metadata_tags, f"SDXL row tags lost structured explicit metadata tag: {required}")
|
||||
|
||||
pair = pb.build_insta_of_pair(
|
||||
row_number=1,
|
||||
@@ -4469,6 +4506,53 @@ def smoke_sdxl_tag_routes() -> None:
|
||||
typed_hard.as_text() == sdxl_formatter._hard_tags(hard_row, pair, 1.29),
|
||||
"Typed SDXL pair hard tag route should match legacy wrapper output",
|
||||
)
|
||||
pair_stale_hard_row = _fixture_hardcore_row(
|
||||
prompt="",
|
||||
item="standing portrait setup",
|
||||
pose="standing pose",
|
||||
role_graph="",
|
||||
source_role_graph="",
|
||||
expression="neutral expression",
|
||||
composition="standing portrait frame",
|
||||
action_family="",
|
||||
position_family="",
|
||||
position_key="",
|
||||
position_keys=[],
|
||||
)
|
||||
pair_stale_root = {
|
||||
"hardcore_prompt": "stale pair prompt says fully nude naked pussy penis oral anal semen penetration",
|
||||
"hardcore_women_count": 1,
|
||||
"hardcore_men_count": 1,
|
||||
}
|
||||
pair_stale_tags = sdxl_tag_routes.hard_tags_result(
|
||||
sdxl_tag_routes.SDXLPairTagRequest(pair_stale_hard_row, pair_stale_root, 1.29),
|
||||
deps,
|
||||
).as_text()
|
||||
for forbidden in ("(naked:", "pussy", "penis", "oral sex", "anal sex", "semen", "penetration"):
|
||||
_expect(forbidden not in pair_stale_tags, f"SDXL pair tags should not infer explicit tag from stale prompt: {forbidden}")
|
||||
|
||||
pair_metadata_root = {
|
||||
"hardcore_prompt": "stale pair prompt without explicit tag anchors",
|
||||
"hardcore_women_count": 1,
|
||||
"hardcore_men_count": 1,
|
||||
"hardcore_clothing_state": "fully nude body, bare skin unobstructed",
|
||||
}
|
||||
pair_metadata_hard_row = _fixture_hardcore_row(
|
||||
prompt="",
|
||||
item="kneeling pose with visible penis and pussy contact",
|
||||
role_graph="penis thrusts into pussy",
|
||||
source_role_graph="penis thrusts into pussy",
|
||||
action_family="penetration",
|
||||
position_family="penetrative",
|
||||
position_key="kneeling",
|
||||
position_keys=["kneeling"],
|
||||
)
|
||||
pair_metadata_tags = sdxl_tag_routes.hard_tags_result(
|
||||
sdxl_tag_routes.SDXLPairTagRequest(pair_metadata_hard_row, pair_metadata_root, 1.29),
|
||||
deps,
|
||||
).as_text()
|
||||
for required in ("(naked:1.29)", "pussy", "penis", "penetration"):
|
||||
_expect(required in pair_metadata_tags, f"SDXL pair tags lost structured explicit metadata tag: {required}")
|
||||
formatted = sdxl_formatter.format_sdxl_prompt(
|
||||
"",
|
||||
metadata_json=_json(pair),
|
||||
|
||||
Reference in New Issue
Block a user