diff --git a/caption_policy.py b/caption_policy.py index 9d3c1ea..5ce845e 100644 --- a/caption_policy.py +++ b/caption_policy.py @@ -51,7 +51,9 @@ ITEM_LABELS = ( ) ACTION_FAMILY_CAPTION_LABELS = { + "anal": "anal action", "foreplay": "foreplay action", + "manual": "manual action", "outercourse": "non-penetrative action", "oral": "oral action", "penetration": "penetrative action", diff --git a/tools/prompt_map_audit.py b/tools/prompt_map_audit.py index c9932aa..c212dfe 100644 --- a/tools/prompt_map_audit.py +++ b/tools/prompt_map_audit.py @@ -23,12 +23,17 @@ if str(ROOT) not in sys.path: import category_template_metadata as template_metadata_policy # noqa: E402 import category_library as category_policy # noqa: E402 +import caption_policy # noqa: E402 import caption_naturalizer # noqa: E402 +import hardcore_action_metadata # noqa: E402 +import hardcore_position_config # noqa: E402 import krea_formatter # noqa: E402 import location_config as location_policy # noqa: E402 import prompt_builder as pb # noqa: E402 import scene_camera_adapters as scene_camera_policy # noqa: E402 import sdxl_formatter # noqa: E402 +import sdxl_presets # noqa: E402 +import sdxl_tag_policy # noqa: E402 POOL_DEFINITION_KEYS = ("scene_pools", "expression_pools", "composition_pools") POOL_REFERENCE_KEYS = { @@ -87,6 +92,9 @@ ALLOWED_PROMPT_ROW_READS: set[tuple[str, str]] = { ("caption_naturalizer.py", "_metadata_to_prose"), } +ROUTE_POLICY_ACTION_EXCLUSIONS = {"default"} +ROUTE_POLICY_POSITION_EXCLUSIONS = {"any"} + def _literal_or_none(node: ast.AST) -> Any: try: @@ -515,6 +523,46 @@ def _effective_category_coverage_errors(paths: list[Path]) -> list[tuple[str, st return errors +def _registered_route_policy_errors() -> list[tuple[str, str, str]]: + errors: list[tuple[str, str, str]] = [] + action_families = sorted( + set(hardcore_action_metadata.HARDCORE_ACTION_FAMILY_CHOICES) + - ROUTE_POLICY_ACTION_EXCLUSIONS + ) + position_families = sorted( + set(hardcore_position_config.hardcore_position_family_choices()) + - ROUTE_POLICY_POSITION_EXCLUSIONS + ) + + for family in action_families: + if not sdxl_presets.SDXL_ACTION_FAMILY_TAGS.get(family): + errors.append(("sdxl_presets.py", f"SDXL_ACTION_FAMILY_TAGS.{family}", "missing SDXL action-family tags")) + if not caption_policy.ACTION_FAMILY_CAPTION_LABELS.get(family): + errors.append(("caption_policy.py", f"ACTION_FAMILY_CAPTION_LABELS.{family}", "missing caption action-family label")) + + for family in position_families: + if not sdxl_presets.SDXL_POSITION_FAMILY_TAGS.get(family): + errors.append(("sdxl_presets.py", f"SDXL_POSITION_FAMILY_TAGS.{family}", "missing SDXL position-family tags")) + if not caption_policy.POSITION_FAMILY_CAPTION_LABELS.get(family): + errors.append(("caption_policy.py", f"POSITION_FAMILY_CAPTION_LABELS.{family}", "missing caption position-family label")) + + valid_scopes = {"action", "position"} + valid_families = { + "action": set(action_families), + "position": set(position_families), + } + for route_key, blocked_tags in sorted(sdxl_tag_policy.INCOMPATIBLE_ROUTE_TAGS.items()): + scope, separator, family = route_key.partition(":") + if separator != ":" or scope not in valid_scopes: + errors.append(("sdxl_tag_policy.py", f"INCOMPATIBLE_ROUTE_TAGS.{route_key}", "invalid route key")) + continue + if family not in valid_families[scope]: + errors.append(("sdxl_tag_policy.py", f"INCOMPATIBLE_ROUTE_TAGS.{route_key}", "unknown route family")) + if not blocked_tags: + errors.append(("sdxl_tag_policy.py", f"INCOMPATIBLE_ROUTE_TAGS.{route_key}", "empty incompatible-tag block")) + return errors + + def _location_theme_camera_profile_errors() -> list[tuple[str, str, str]]: errors: list[tuple[str, str, str]] = [] profile_keys = set(scene_camera_policy.SCENE_CAMERA_PROFILE_KEYS) @@ -954,6 +1002,13 @@ def main() -> int: return 1 print("OK: category routes define effective item, scene, expression, composition, and route metadata coverage.") + print("\n# Registered Route Policy Validation") + registered_route_errors = _registered_route_policy_errors() + if registered_route_errors: + print_table(("Source", "Path", "Issue"), registered_route_errors) + return 1 + print("OK: registered route families have SDXL tags, caption labels, and valid incompatibility filters.") + print("\n# Location Theme Camera Profile Validation") location_profile_errors = _location_theme_camera_profile_errors() if location_profile_errors: diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index bc90331..4747423 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -4015,6 +4015,10 @@ def smoke_caption_policy() -> None: ) row = {"action_family": "oral", "position_family": ""} _expect(caption_policy.metadata_action_label(row) == "oral action", "Caption action-family label changed") + row = {"action_family": "anal", "position_family": ""} + _expect(caption_policy.metadata_action_label(row) == "anal action", "Caption anal action-family label changed") + row = {"action_family": "manual", "position_family": ""} + _expect(caption_policy.metadata_action_label(row) == "manual action", "Caption manual action-family label changed") row = {"action_family": "threesome", "position_family": ""} _expect(caption_policy.metadata_action_label(row) == "three-person action", "Caption threesome action-family label changed") row = {"action_family": "group", "position_family": ""}