A seed of -1 means random, as it does everywhere else

Omitting the seed was already random. Passing -1 was not: it went through the
uint64 wrap and arrived as 18446744073709551615, which is a perfectly valid
*fixed* seed -- so "give me something new" returned the identical picture every
time, silently, and the retry loop would have redrawn the same rejected image
until it ran out of attempts.

-1 is what ComfyUI's own interface uses for random, and A1111, and everything
else that has ever asked somebody for a seed. A model that has read any of them
will write it, so the one reading that had to work was the one that did not.

Any negative value, not only -1, because the sentinel is the *idea* rather than
the number and a model that writes -2 means the same thing. Zero stays a real
seed: it is the boundary this change could easily have swallowed, and it is one
somebody deliberately picks.

Confirmed against the real ComfyUI: -1 now sends a random uint64 that it accepts
and draws from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaroslav Beneš
2026-08-05 14:23:38 +02:00
parent 5e75948069
commit ca7eb6cedb
5 changed files with 41 additions and 4 deletions
+4 -1
View File
@@ -75,7 +75,10 @@ SCHEMA: dict[str, Any] = {
},
"seed": {
"type": "integer",
"description": "Omit for a new random image; repeat one to get the same image again.",
"description": (
"Omit it, or pass -1, for a new random image. Repeat a seed you were "
"told about to get the same image again."
),
},
"steps": {"type": "integer", "description": "Sampling steps. Default 20."},
"cfg": {"type": "number", "description": "Prompt adherence. Default 8."},
+9 -1
View File
@@ -86,13 +86,21 @@ def resolve(given: dict[str, Any]) -> dict[str, Any]:
Absent and null are both "no opinion". A model that emits `"seed": null`
rather than omitting the key is common enough that treating it as a request
for seed zero would be a bug nobody could see.
**A negative seed means random**, which is what `-1` means in ComfyUI's own
interface, in A1111, and in every other thing that has ever asked somebody
for a seed. A model that has read any of them will write it, and without
this it went through the uint64 wrap and came out as 18446744073709551615 --
a perfectly valid *fixed* seed, so "give me something new" produced the same
picture every time. Exactly the wrong answer, arrived at silently.
"""
values: dict[str, Any] = {**DEFAULTS}
for name, value in (given or {}).items():
if name in PLACEHOLDERS and value is not None and value != "":
values[name] = value
values["seed"] = _whole(values.get("seed"), default=random_seed()) % (MAX_SEED + 1)
seed = _whole(values.get("seed"), default=-1)
values["seed"] = random_seed() if seed < 0 else seed % (MAX_SEED + 1)
for name in ("steps", "width", "height"):
values[name] = _clamp(_whole(values.get(name), DEFAULTS[name]), name)
for name in ("cfg", "denoise"):