Nodes/Orel's Nodes/Format Text
ComfyUI Node

Format Text

Prompts and reports from a template, without eval()

By DarkOrel·Created 30 days ago·Updated 22 days ago· 1
Format Text
  • a
  • b
  • c
  • d
  • e
  • f
  • text
◄templateWorkflow Done Face similarity: {a}% LoRA: {b}►
◄missing_placeholder►

Sooner or later you want the workflow to say something rather than just make something. "Face similarity: 62.4% - LoRA: ch9ractername - seed 8842190." A caption, a report line, a filename, a piece of a prompt assembled from parts.

Format Text does that with six inputs and a text box, and it does it without a Popen-shaped hole in the middle of your graph.

How the templating works

You write a multiline template. Placeholders are {a} through {f}, and each one maps to the numbered input socket of the same name. The author's own default template is a decent illustration:

Workflow Done
Face similarity: {a}%
LoRA: {b}

Feed a percentage into a, a LoRA name into b, get text out.

The inputs are declared as * (ComfyUI's wildcard), so you can wire an INT, FLOAT or STRING straight in without a converter in front of every one. Values get stringified on the way through, using the same rules as the pack's Any to String node: None becomes empty, booleans become True/False.

Two details that matter and aren't obvious:

Empty strings count as missing. A connected value that is empty is treated the same as an unconnected socket. Given how often a passed-through string ends up empty, this saves you a lot of duplicated separators.

Any {word} is a placeholder, not just a–f. The substitution is a regex over {identifier}, so {seed} in your template is looked up in the value map, isn't found, and gets replaced by the missing_placeholder value. That's mostly a good thing - it means you can name placeholders readably - but it also means you can't type a literal {thing} in your output text. There's no brace escaping. Literal { characters that don't form a valid identifier are left alone, so { 42 } survives fine.

Inputs and outputs

  • template (STRING, required, multiline) - the text with placeholders.
  • a … f (optional, type *) - the six values.
  • missing_placeholder (STRING, optional, default empty) - what a placeholder with nothing wired into it becomes.
  • text (STRING) - the finished string.

Set missing_placeholder to N/A or - if you'd rather see that a field was empty than get a blank hole in a caption.

Why the no-eval thing matters

There are text nodes in this ecosystem that evaluate Python expressions, and they're legitimately powerful and legitimately a bad idea in a shared workflow. ComfyUI's model is that workflows are exchanged freely - they're attached to images, posted to Civitai, pasted into Discord. A node that runs arbitrary expressions in your Python process is remote code execution with extra steps, and there's no review process for custom nodes to catch it.

Format Text's substitution is a fixed regex and a dict lookup. There's no expression language, no attribute access, no way to reach out of the node. If your template says {__import__}, you get your missing_placeholder back. That's the entire security model, and it's the right trade for the "build a caption" use case.

Install

Manager → search Orel's Nodes → Install → restart → hard-refresh. Manual:

cd ComfyUI/custom_nodes
git clone https://github.com/DarkOrel/Orels-Nodes.git orels-nodes
python -m pip install -r orels-nodes/requirements.txt

Restart and hard-refresh. Nothing in that requirements file is needed for text nodes - it's for the pack's Hugging Face features.

Gotchas

Missing placeholders produce an empty gap, not a warning. If your output reads LoRA: with nothing after it, the value was empty or unconnected. There's no log line telling you.

Whitespace is your problem. There's no join, no trimming, no separator handling. Conditionally-blank fields leave you with Face similarity: % and no way to suppress the label. If you need conditional text, put the whole label and the placeholder in the template and accept the gap, or route around it with a switch.

Multiline means literal newlines. Whether that's fine depends entirely on the consumer. Fine for a caption. Less fine if the string is going into a single-line filename widget - see Filename Builder in this same pack for that job.

CategoryOrel's Nodes/Text

Inputs (8)

NameTypeDefaultDescription
templateSTRINGWorkflow Done Face similarity: {a}% LoRA: {b}—
aopt*—
bopt*—
copt*—
dopt*—
eopt*—
fopt*—
missing_placeholderoptSTRINGReplacement used when a placeholder has no connected value.

Outputs (1)

NameTypeDescription
textSTRING—