feat(inspect): port legacy parser rules the new resolver missed (upstream nodes)
R6 and R9 for nodes not installed beside ComfyUI here. Their input names and output slots come from each pack's source at the commit the saved workflows record in the node's cnr_id and ver properties (the task report lists repository, commit, file and line): - samplers ClownsharKSampler_Beta (RES4LYF), KSampler (Efficient) and ttN KSampler_v2 (latent input "latent"); - pass-throughs SeedVarianceEnhancer and ApplyInstantIDAdvanced (positive 1, negative 2; output 0 is the model); - selector Switch conditioning [Crystools]; - encoder smZ CLIPTextEncode: its text when with_SDXL is false; when true the connected CLIP decides at run time which inputs are read, so the prompt is known only when text, text_g and text_l agree; - strings ttN text, CR Text, CR Prompt Text and TextBoxMira; - facts model pass-through CR Apply LoRA Stack. Inputs that replace what a sampler reads are not followed: RES4LYF options (may carry a guider, and with sigmas replace the settings), an Efficiency script and a ttN XY plot leave the prompt unsupported and the facts unresolved. The overwritten PromptManager refusal (R1) still holds behind SeedVarianceEnhancer. The default profile does not read the table.
This commit is contained in:
@@ -41,7 +41,7 @@ STRATEGIES: Mapping[str, FrozenSet[str]] = MappingProxyType(
|
||||
"conditioning": frozenset({"passthrough", "zeroed"}),
|
||||
"selectors": frozenset({"boolean-select"}),
|
||||
"composites": frozenset({"combine"}),
|
||||
"encoders": frozenset({"single", "parts-equal", "pm-compose"}),
|
||||
"encoders": frozenset({"single", "parts-equal", "pm-compose", "flag-parts"}),
|
||||
"strings": frozenset({"literal", "strip-newlines", "join-multi", "concat"}),
|
||||
"savers": frozenset({"comfy-counter", "vhs-counter"}),
|
||||
}
|
||||
@@ -54,7 +54,7 @@ SAVER_PATTERNS = MappingProxyType(
|
||||
"vhs-counter": "{name}_{counter:05}.{ext}",
|
||||
}
|
||||
)
|
||||
_SAMPLER_OPTIONS = frozenset({"prompt_overrides"})
|
||||
_SAMPLER_OPTIONS = frozenset({"prompt_overrides", "fact_overrides"})
|
||||
_CONFIGURATION_KINDS = frozenset({"noise", "sampler", "sigmas", "guider"})
|
||||
_SCALAR_KINDS = frozenset({"number", "string"})
|
||||
_TYPE_FIELDS = frozenset({"type_fields"})
|
||||
@@ -183,8 +183,10 @@ def _check_sampler(entry: dict, strategy: str, where: str) -> None:
|
||||
_name(entry[stage], where)
|
||||
# Inputs that, when set, replace the prompt the sampler's conditioning
|
||||
# inputs carry (a detailer's wildcard); the prompt is then not read.
|
||||
if "prompt_overrides" in entry:
|
||||
_names(entry["prompt_overrides"], where + ".prompt_overrides")
|
||||
# ``fact_overrides`` (read by comfy-metadata.js): inputs that, when set,
|
||||
# replace the settings the facts would read (a script or an XY plot).
|
||||
for option in sorted(_SAMPLER_OPTIONS & entry.keys()):
|
||||
_names(entry[option], f"{where}.{option}")
|
||||
keys = {"guider"} if strategy == "custom-advanced" else {"positive", "negative"}
|
||||
conditioning = entry["conditioning"]
|
||||
_fields(conditioning, where + ".conditioning", frozenset(keys), frozenset())
|
||||
@@ -246,6 +248,16 @@ def _check_conditioning(entry: dict, strategy: str, where: str) -> None:
|
||||
|
||||
|
||||
def _check_encoder(entry: dict, strategy: str, where: str) -> None:
|
||||
if strategy == "flag-parts":
|
||||
# A literal boolean ``flag`` false reads the first input; true needs
|
||||
# every input equal (smZ CLIPTextEncode's with_SDXL).
|
||||
_fields(entry, where, frozenset({"strategy", "inputs", "params"}), frozenset())
|
||||
params = entry["params"]
|
||||
if not isinstance(params, dict) or set(params) != {"flag"}:
|
||||
raise SemanticsError(f"{where}: params must be exactly ['flag']")
|
||||
_name(params["flag"], where + ".params")
|
||||
_names(entry["inputs"], where, minimum=2)
|
||||
return
|
||||
_fields(entry, where, frozenset({"strategy", "inputs"}), frozenset({"compose"}))
|
||||
_names(entry["inputs"], where, minimum=2 if strategy == "parts-equal" else 1)
|
||||
if "compose" in entry and not isinstance(entry["compose"], bool):
|
||||
|
||||
@@ -759,6 +759,8 @@ class _GraphResolver:
|
||||
names = entry["inputs"]
|
||||
if entry["strategy"] == "single":
|
||||
return self.text(inputs.get(names[0]), "string", depth + 1)
|
||||
if entry["strategy"] == "flag-parts":
|
||||
return self.flagged(node_id, entry, inputs, depth)
|
||||
if entry["strategy"] != "parts-equal":
|
||||
raise self.unsupported("Unsupported encoder strategy", node_id)
|
||||
parts = [self.text(inputs.get(name), "string", depth + 1) for name in names]
|
||||
@@ -771,6 +773,24 @@ class _GraphResolver:
|
||||
return self.joined_paths(filled, parts)
|
||||
raise self.named("Encoder parts have different texts", "composite", node_id)
|
||||
|
||||
def flagged(
|
||||
self, node_id: str, entry: Mapping, inputs: dict, depth: int
|
||||
) -> Tuple[str, list, str]:
|
||||
"""smZ CLIPTextEncode: ``text`` unless its SDXL flag may apply.
|
||||
|
||||
With the literal flag false the first input is encoded. With it true,
|
||||
the connected CLIP's model class decides at run time which inputs are
|
||||
read, so the prompt is known only when every input is the same text.
|
||||
"""
|
||||
names = entry["inputs"]
|
||||
if not self.setting(node_id, inputs, entry["params"]["flag"], bool):
|
||||
return self.text(inputs.get(names[0]), "string", depth + 1)
|
||||
parts = [self.text(inputs.get(name), "string", depth + 1) for name in names]
|
||||
self.scan(sum(len(part[0]) for part in parts))
|
||||
if len({part[0] for part in parts}) == 1:
|
||||
return self.joined_paths(parts, parts)
|
||||
raise self.named(_RUNTIME_REASON, "runtime-value", node_id)
|
||||
|
||||
def stringed(
|
||||
self, node_id: str, entry: Mapping, inputs: dict, depth: int
|
||||
) -> Tuple[str, list, str]:
|
||||
|
||||
Reference in New Issue
Block a user