Files
Artificial-Sweetener-Simple…/tools/prompt_control_characterization/workflow.py
T

136 lines
4.3 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
"""Build Prompt Control characterization graphs from public Comfy nodes."""
from __future__ import annotations
import json
from dataclasses import dataclass
from tools.anima_workflow_graph import AnimaWorkflowGraph, NodeReference
from tools.comfy_api import JsonObject
from tools.comfy_integration.anima_fixture_selections import (
ANIMA_DIFFUSION_SELECTION,
ANIMA_TEXT_ENCODER_SELECTION,
ANIMA_VAE_SELECTION,
)
from .cases import PromptControlCase
from .conditioning import (
PROMPT_CONTROL_TEXT_CONDITIONING_BUILDER,
expansion_inputs,
)
Workflow = dict[str, JsonObject]
@dataclass(frozen=True)
class BuiltPromptControlWorkflow:
"""Return one graph and its two evidence-producing node identities."""
prompt: Workflow
expansion_node_id: str
snapshot_node_id: str
runtime_node_id: str
class PromptControlWorkflowBuilder:
"""Compose public Prompt Control nodes with native Comfy sampling."""
def build(self, case: PromptControlCase) -> BuiltPromptControlWorkflow:
"""Build one complete loader-to-runtime-evidence API graph."""
graph = AnimaWorkflowGraph()
loader = graph.add(
"SimpleSyrup.SimpleLoadAnima",
diffusion_model=ANIMA_DIFFUSION_SELECTION,
quantization="Original",
diffusion_weight_dtype="default",
text_encoder=ANIMA_TEXT_ENCODER_SELECTION,
text_encoder_device="default",
vae=ANIMA_VAE_SELECTION,
)
expansion_spec = expansion_inputs(case)
expansion = graph.add(
"SimpleSyrupBenchmark.SnapshotPromptControlExpansion",
case_id=case.case_id,
text=expansion_spec.text,
capture_text_expansion=expansion_spec.capture_text_expansion,
lora_text=case.lora_text,
)
model: NodeReference = [loader, 0]
clip: NodeReference = [loader, 1]
lora_loader: str | None = None
if case.lora_text:
lora_loader = graph.add(
"PCLazyLoraLoaderAdvanced",
model=model,
clip=clip,
text=case.lora_text,
apply_hooks=True,
tags="",
start=0.0,
end=1.0,
num_steps=0,
)
model = [lora_loader, 0]
clip = [lora_loader, 1]
positive = PROMPT_CONTROL_TEXT_CONDITIONING_BUILDER.add(
graph,
case,
clip=clip,
)
negative_encode = graph.add(
"PCTextEncodeWithRange",
clip=clip,
text="low quality",
start=0.0,
end=1.0,
)
identities = [adapter.slot for adapter in case.expected_adapters]
snapshot_inputs: dict[str, object] = {
"positive": positive,
"negative": [negative_encode, 0],
"case_id": case.case_id,
"adapter_identities_json": json.dumps(identities),
}
if lora_loader is not None and case.expected_adapters:
snapshot_inputs["hooks"] = [lora_loader, 2]
snapshot = graph.add(
"SimpleSyrupBenchmark.SnapshotPromptControl", **snapshot_inputs
)
instrument = graph.add(
"SimpleSyrupBenchmark.InstrumentPromptControlModel",
model=model,
run_id=case.case_id,
adapter_identities_json=json.dumps(identities),
)
latent = graph.add(
"EmptyCosmosLatentVideo",
width=512,
height=512,
length=1,
batch_size=1,
)
sampled = graph.add(
"KSampler",
model=[instrument, 0],
seed=1_029_384_756,
steps=8,
cfg=4.0,
sampler_name="er_sde",
scheduler="simple",
positive=[snapshot, 0],
negative=[snapshot, 1],
latent_image=[latent, 0],
denoise=1.0,
)
runtime = graph.add(
"SimpleSyrupBenchmark.ReadPromptControlRuntime",
latent=[sampled, 0],
run_id=case.case_id,
)
return BuiltPromptControlWorkflow(graph.prompt, expansion, snapshot, runtime)