Judge: add seed (reproducible output) + interrupt support
New 'seed' input seeds sampling (torch.manual_seed before generation) so a fixed seed reproduces the same output — no reroll when re-queueing while tweaking downstream nodes; bump it to reroll (named 'seed' so the frontend adds control_after_generate). Generation now honors ComfyUI cancel: a StoppingCriteria halts model.generate() promptly and _check_interrupt() raises between passes. Workflows updated with the seed widget. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+38
-1
@@ -453,8 +453,33 @@ def _apply_template(processor, messages, think=True):
|
||||
return _format_chatml_qwenvl(messages)
|
||||
|
||||
|
||||
def _check_interrupt():
|
||||
"""Raise if the user hit ComfyUI's cancel — aborts the node between passes."""
|
||||
try:
|
||||
import comfy.model_management as mm
|
||||
mm.throw_exception_if_processing_interrupted()
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def _interrupt_stopping():
|
||||
"""StoppingCriteria that halts generate() promptly when ComfyUI is interrupted."""
|
||||
try:
|
||||
import comfy.model_management as mm
|
||||
from transformers import StoppingCriteria, StoppingCriteriaList
|
||||
|
||||
class _Interrupt(StoppingCriteria):
|
||||
def __call__(self, input_ids, scores, **kw):
|
||||
return mm.processing_interrupted()
|
||||
|
||||
return StoppingCriteriaList([_Interrupt()])
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _generate_from_messages(model, processor, messages, images, max_new_tokens, temperature, think=True):
|
||||
"""Template + forward pass for a chat-message list; returns the decoded string."""
|
||||
_check_interrupt()
|
||||
text = _apply_template(processor, messages, think)
|
||||
inputs = processor(text=[text], images=images, return_tensors="pt")
|
||||
inputs = inputs.to(model.device)
|
||||
@@ -464,6 +489,9 @@ def _generate_from_messages(model, processor, messages, images, max_new_tokens,
|
||||
gen_kwargs.update(do_sample=True, temperature=float(temperature))
|
||||
else:
|
||||
gen_kwargs.update(do_sample=False)
|
||||
sc = _interrupt_stopping()
|
||||
if sc is not None:
|
||||
gen_kwargs["stopping_criteria"] = sc
|
||||
|
||||
with torch.inference_mode():
|
||||
out = model.generate(**inputs, **gen_kwargs)
|
||||
@@ -821,6 +849,9 @@ class QwenVLImageJudge:
|
||||
"precision": (["bf16", "fp8", "nf4"], {"default": "bf16"}),
|
||||
"max_new_tokens": ("INT", {"default": 3072, "min": 64, "max": 8192}),
|
||||
"temperature": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.5, "step": 0.05}),
|
||||
# Seeds sampling so a fixed seed reproduces the same output (no reroll when
|
||||
# you re-queue while tweaking downstream nodes). Bump it to reroll.
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}),
|
||||
"swap_eval": ("BOOLEAN", {"default": True}),
|
||||
# Reasoning models (Qwen3.5/3.6) judge verdicts FAR better with thinking on
|
||||
# (off -> they rubber-stamp 'match'). Costs more tokens; raise max_new_tokens.
|
||||
@@ -849,7 +880,7 @@ class QwenVLImageJudge:
|
||||
}
|
||||
|
||||
def judge(self, reference_image, mode, model_path, precision,
|
||||
max_new_tokens, temperature, swap_eval, profile="general",
|
||||
max_new_tokens, temperature, swap_eval, seed=0, profile="general",
|
||||
enable_thinking=True, json_output=False, model_select=MANUAL_CHOICE,
|
||||
generated_image=None, keep_loaded=True, auto_download=True,
|
||||
report_dir="", run_tag="", axes="", reference_description="",
|
||||
@@ -888,6 +919,12 @@ class QwenVLImageJudge:
|
||||
ref_pil = _tensor_to_pil(reference_image)
|
||||
model, processor = _load_model(resolved_path, eff_precision)
|
||||
|
||||
# Seed sampling so a fixed seed reproduces the same output; check for cancel.
|
||||
torch.manual_seed(int(seed))
|
||||
if torch.cuda.is_available():
|
||||
torch.cuda.manual_seed_all(int(seed))
|
||||
_check_interrupt()
|
||||
|
||||
if mode == "chat":
|
||||
gen_pil = _tensor_to_pil(generated_image) if generated_image is not None else None
|
||||
return self._chat(model, processor, ref_pil, gen_pil, system_prompt, user_prompt,
|
||||
|
||||
Reference in New Issue
Block a user