Files
ComfyUI-Prompt-Calibrator/nodes/receptor.py
T
Ethanfel f5be04a5cb Fix: render text inputs as editable fields, not input sockets
Widget-type inputs in the optional section render as connection sockets (not
editable boxes) in some ComfyUI frontends. Moved all widgets (report_dir, run_tag,
reference_description, system_prompt, user_prompt, keep_loaded, auto_download) into
required; only generated_image (a real node-to-node wire) stays optional. Same fix
for the receptor's source_file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 10:18:12 +02:00

66 lines
2.6 KiB
Python

"""
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)"
}