Files
Artificial-Sweetener-Simple…/tools/anima_attention_coupling_integration/schema.py
T

81 lines
3.2 KiB
Python

# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
# Copyright (C) 2026 Artificial Sweetener and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Validate the exact live P5.8 public-node metadata contract."""
from __future__ import annotations
from typing import cast
from tools.comfy_api import JsonObject
from .matrix import PUBLIC_NODE_ID
EXPECTED_INPUTS = (
"model",
"seed",
"steps",
"cfg",
"sampler_name",
"scheduler",
"positive",
"negative",
"region_masks",
"regional_prompt_weight",
"region_mask_feather",
"latent_image",
"denoise",
)
def validate_public_node_metadata(metadata: JsonObject) -> None:
"""Reject metadata that does not prove the shipped public schema."""
if metadata.get("name") != PUBLIC_NODE_ID:
raise ValueError("Live Attention Coupling node ID does not match P5.8.")
if metadata.get("display_name") != "KSampler (Attention Coupling)":
raise ValueError("Live Attention Coupling display name does not match P5.8.")
if metadata.get("category") != "SimpleSyrup/Sampling":
raise ValueError("Live Attention Coupling category does not match P5.8.")
order = _object(metadata.get("input_order"), "input_order").get("required")
if not isinstance(order, list) or tuple(order) != EXPECTED_INPUTS:
raise ValueError("Live Attention Coupling input order does not match P5.8.")
required = _object(
_object(metadata.get("input"), "input").get("required"), "required"
)
if tuple(required) != EXPECTED_INPUTS:
raise ValueError("Live Attention Coupling required inputs do not match P5.8.")
expected_types = {
"model": "MODEL",
"positive": "CONDITIONING,CONDITIONING_BATCH",
"negative": "CONDITIONING,CONDITIONING_BATCH",
"region_masks": "MASK",
"latent_image": "LATENT",
}
for name, expected_type in expected_types.items():
descriptor = required[name]
if not isinstance(descriptor, list) or descriptor[0] != expected_type:
raise ValueError(f"Live input {name!r} does not expose {expected_type}.")
if metadata.get("output") != ["LATENT"]:
raise ValueError("Live Attention Coupling output type does not match P5.8.")
description = metadata.get("description")
if not isinstance(description, str) or not all(
word in description.lower() for word in ("lora", "schedule", "overlap")
):
raise ValueError("Live description omits required regional LoRA guidance.")
for name in ("model", "positive", "negative", "region_masks"):
descriptor = cast(list[object], required[name])
options = _object(descriptor[1], f"{name} options")
tooltip = options.get("tooltip")
if not isinstance(tooltip, str) or "lora" not in tooltip.lower():
raise ValueError(f"Live input {name!r} omits regional LoRA guidance.")
def _object(value: object, label: str) -> JsonObject:
"""Narrow one dynamic metadata value to a string-keyed object."""
if not isinstance(value, dict) or not all(isinstance(key, str) for key in value):
raise TypeError(f"Live Attention Coupling {label} must be an object.")
return cast(JsonObject, value)