""" Calibrator Prompt Receptor node. The injection point for the external CLI-agent controller. The agent overrides this node's widget values per queue via the ComfyUI HTTP API (`POST /prompt`, override by node id), or — as a fallback — points `source_file` at a JSON file the agent writes. Its outputs feed the T2I sampler in place of a static prompt. This is the "receptor in ComfyUI" in the loop: agent -> (sets prompt here) -> T2I -> Qwen3-VL Judge -> analysis -> agent """ from __future__ import annotations import json import os class CalibratorPromptReceptor: CATEGORY = "prompt_calibrator" FUNCTION = "emit" RETURN_TYPES = ("STRING", "STRING", "INT") RETURN_NAMES = ("prompt", "negative", "seed") @classmethod def INPUT_TYPES(cls): return { "required": { "prompt": ("STRING", {"default": "", "multiline": True}), "negative": ("STRING", {"default": "", "multiline": True}), "seed": ("INT", {"default": 0, "min": 0, "max": 0x7FFFFFFFFFFFFFFF}), # If set and present, a JSON file {prompt, negative, seed} overrides # the widgets above. Lets the agent drive the loop file-first if it # prefers that to the HTTP API. (Kept in `required` so it renders as an # editable field, not an input socket.) "source_file": ("STRING", {"default": ""}), }, } @classmethod def IS_CHANGED(cls, prompt, negative, seed, source_file=""): # Re-run whenever the effective inputs change: widget values (API override) # OR the source file's mtime (file-driven mode). mtime = "" if source_file and os.path.isfile(source_file): mtime = str(os.path.getmtime(source_file)) return f"{prompt}|{negative}|{seed}|{source_file}|{mtime}" def emit(self, prompt, negative, seed, source_file=""): if source_file and os.path.isfile(source_file): try: with open(source_file, "r", encoding="utf-8") as f: data = json.load(f) prompt = data.get("prompt", prompt) negative = data.get("negative", negative) seed = int(data.get("seed", seed)) except (OSError, ValueError, json.JSONDecodeError) as e: print(f"[CalibratorPromptReceptor] could not read {source_file}: {e}") return (prompt, negative, int(seed)) NODE_CLASS_MAPPINGS = {"CalibratorPromptReceptor": CalibratorPromptReceptor} NODE_DISPLAY_NAME_MAPPINGS = { "CalibratorPromptReceptor": "SxCP External Prompt (Receptor)" }