feat: GGUF loader support, and fix silent pipeline-editor state loss
GGUF: port v4's gguf_support.py (backend selection, content-based .gguf detection, dequantize fallback) and wire it into both the diffusion-model and CLIP loaders. Pipeline editor: reopening the Models-and-pipeline settings window was silently discarding the whole edited pipeline (a loaded preset, every loader file, every sampler setting) because GET /api/pipeline is deliberately stateless server-side and boot.js never fed its own already-saved slots back into a freshly remounted editor -- found live while walking through the MiniMax-H3 setup flow. Fixed by re-validating the saved slots through check() on reopen instead of always replaying the raw stateless default. Also fixes, found via the extensive_testing adversarial loop over both of the above: a stale file-listing cache in the pipeline window (no way to see a newly-added model file without a full page reload), and a chain of races/leaks in its own refresh/commit error-banner handling that surfaced across five review rounds -- a rejection from a superseded request overwriting a fresher success, a failure banner never clearing on success, banners bleeding across groups on navigation, and a commit()'s refusal landing on the wrong view after the user backs out before it answers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
4744d30087
commit
7a226830b1
@@ -11,11 +11,24 @@ import folder_paths
|
||||
from comfy_api.latest import io
|
||||
|
||||
from ..._core import log
|
||||
from .. import gguf_support
|
||||
from ..common import (COMPUTE_DTYPES, WEIGHT_DTYPES, attention_choices,
|
||||
attention_override, dtype_of, set_fp16_accumulation,
|
||||
weight_model_options)
|
||||
|
||||
|
||||
def _model_file_choices():
|
||||
"""Diffusion model files, `.gguf` included.
|
||||
|
||||
Core's extension set has no `.gguf`, so those files are on disk and
|
||||
invisible to every picker. Appended rather than merged in sorted order, so
|
||||
an existing pipeline's saved choice keeps its position in the list and
|
||||
nothing a user already picked moves.
|
||||
"""
|
||||
return list(folder_paths.get_filename_list("diffusion_models")) + \
|
||||
gguf_support.gguf_names("diffusion_models")
|
||||
|
||||
|
||||
class FunPackDiffusionModelLoader(io.ComfyNode):
|
||||
@classmethod
|
||||
def define_schema(cls) -> io.Schema:
|
||||
@@ -25,7 +38,7 @@ class FunPackDiffusionModelLoader(io.ComfyNode):
|
||||
category="FunPack/Loaders",
|
||||
description="Load a diffusion model, choosing precision and attention backend.",
|
||||
inputs=[
|
||||
io.Combo.Input("model_name", options=folder_paths.get_filename_list("diffusion_models")),
|
||||
io.Combo.Input("model_name", options=_model_file_choices()),
|
||||
io.Combo.Input("weight_dtype", options=WEIGHT_DTYPES, default="default",
|
||||
tooltip="How the weights are stored. 'default' keeps whatever "
|
||||
"the file already is."),
|
||||
@@ -57,8 +70,31 @@ class FunPackDiffusionModelLoader(io.ComfyNode):
|
||||
model_options = weight_model_options(weight_dtype)
|
||||
notes.append(f"weight dtype: {weight_dtype}")
|
||||
|
||||
path = folder_paths.get_full_path_or_raise("diffusion_models", model_name)
|
||||
state_dict, metadata = comfy.utils.load_torch_file(path, return_metadata=True)
|
||||
if gguf_support.is_gguf(model_name):
|
||||
path = gguf_support.gguf_path("diffusion_models", model_name)
|
||||
if not path:
|
||||
raise RuntimeError(f"{model_name} is no longer where it was listed from.")
|
||||
misnamed = False
|
||||
else:
|
||||
path = folder_paths.get_full_path_or_raise("diffusion_models", model_name)
|
||||
# The CONTENT decides, not the extension. A .gguf renamed to
|
||||
# .safetensors used to reach the safetensors parser and fail with
|
||||
# a UTF-8 decode error from reading a binary header as JSON --
|
||||
# true, and no help at all.
|
||||
misnamed = gguf_support.has_gguf_magic(path)
|
||||
if gguf_support.is_gguf(model_name) or misnamed:
|
||||
if misnamed:
|
||||
notes.append(f"{model_name} is named .safetensors but is a GGUF "
|
||||
f"container -- loaded as GGUF")
|
||||
state_dict, gguf_options, gguf_note = gguf_support.load_state_dict(path)
|
||||
# The quantized path needs its own torch operations, and they must
|
||||
# not be lost to the dtype options above -- a GGUF loaded with
|
||||
# stock ops would try to matmul block-quantized storage.
|
||||
model_options = {**model_options, **gguf_options}
|
||||
metadata = None
|
||||
notes.append(gguf_note)
|
||||
else:
|
||||
state_dict, metadata = comfy.utils.load_torch_file(path, return_metadata=True)
|
||||
model = comfy.sd.load_diffusion_model_state_dict(
|
||||
state_dict, model_options=model_options, metadata=metadata)
|
||||
if model is None:
|
||||
|
||||
Reference in New Issue
Block a user