### New: EvaluateStringMultiline [m9] (`utilities/primitive`)
A multiline string primitive, like the stock String (Multiline), that also:
- resolves `{ a | b }` choices, using the same rules as StepReplace
- takes a `seed_optional`, the same as StepReplace: `0` re-rolls every run, any other seed repeats its result
- has a `random_line` output that picks one line of the resolved text. Blank lines are never picked, and a single line is always returned
### Changed: StepReplace [m9] picks each choice once
Every `{choice}` is now resolved before any search/replace runs: the incoming text first, then each `replace` field. A placeholder used several times now gets the same pick everywhere. For example, `[EYE_COLOR]` replaced with `{ blue | green }` gives all blue or all green, never a mix. Later steps can still match text an earlier step inserted.
45 lines
1.6 KiB
Python
45 lines
1.6 KiB
Python
# Choice evaluation and random line picking for EvaluateStringMultiline [m9].
|
|
# Deliberately free of ComfyUI imports so the logic can be exercised standalone,
|
|
# the same way m_stepreplace.py can be.
|
|
|
|
import random
|
|
|
|
# Relative inside the package (how ComfyUI loads it), plain when run from the checkout.
|
|
try:
|
|
from .m_stepreplace import expand_choices
|
|
except ImportError:
|
|
from m_stepreplace import expand_choices
|
|
|
|
|
|
def pick_line(inText, inRng=None):
|
|
"""One randomly chosen line of the text, or "" when it has none.
|
|
|
|
Lines are trimmed, and blank or whitespace-only lines are never picked, so a
|
|
trailing newline or a spacer line can't come back as an empty result. With a
|
|
single non-blank line, that line is always the one returned.
|
|
"""
|
|
if inRng is None:
|
|
inRng = random.Random()
|
|
|
|
lines = [line.strip() for line in str(inText).splitlines()]
|
|
lines = [line for line in lines if line != ""]
|
|
|
|
if len(lines) == 0:
|
|
return ""
|
|
if len(lines) == 1:
|
|
# No draw, so a one-line value doesn't consume a number from the generator.
|
|
return lines[0]
|
|
return inRng.choice(lines)
|
|
|
|
|
|
def evaluate_string(inText, inSeed=None):
|
|
"""Resolve every { a | b } in the text, then pick one line of the result.
|
|
|
|
Returns (text, random_line). The line is drawn from the evaluated text, so it
|
|
always matches a line of the text output. inSeed of None seeds from entropy; any
|
|
other value makes both outputs reproducible. One generator serves both steps.
|
|
"""
|
|
rng = random.Random(inSeed)
|
|
text = expand_choices(inText, rng)
|
|
return (text, pick_line(text, rng))
|