From c091b30faf3dcda2fb4ea6e9ba277e9f07ef8751 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 16 May 2026 12:46:38 +0200 Subject: [PATCH] Fix GGUF workflow compatibility and release v1.0.4 --- AILab_QwenVL_GGUF.py | 18 +++++++---- pyproject.toml | 2 +- tests/test_new_features.py | 63 ++++++++++++++++++++++++++++++++++++++ update.md | 10 ++++++ 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/AILab_QwenVL_GGUF.py b/AILab_QwenVL_GGUF.py index c00fc82..a9c6b6e 100644 --- a/AILab_QwenVL_GGUF.py +++ b/AILab_QwenVL_GGUF.py @@ -1498,18 +1498,20 @@ class ThinkingLLM_QwenVL_GGUF_Advanced(QwenVLGGUFBase): "frame_count": ("INT", {"default": 16, "min": 1, "max": 64}), "ctx": ("INT", {"default": 32768, "min": 1024, "max": 262144, "step": 512}), "n_batch": ("INT", {"default": 512, "min": 64, "max": 32768, "step": 64}), - "n_ubatch": ("INT", {"default": 512, "min": 32, "max": 32768, "step": 32, "tooltip": "Physical batch size. Keep at or below n_batch. Lower values can help throughput or memory stability depending on the backend."}), "gpu_layers": ("INT", {"default": -1, "min": -1, "max": 200}), "image_max_tokens": ("INT", {"default": 4096, "min": 256, "max": 1024000, "step": 256}), "top_k": ("INT", {"default": 20, "min": 0, "max": 32768}), "pool_size": ("INT", {"default": 4194304, "min": 1048576, "max": 10485760, "step": 524288}), + "keep_model_loaded": ("BOOLEAN", {"default": False}), + "seed": ("INT", {"default": 1, "min": 1, "max": 2**32 - 1}), + "legacy_seed_mode": (["fixed", "randomize", "increment", "decrement"], {"default": "fixed", "tooltip": "Legacy workflow compatibility only. This widget is ignored by current ThinkingLLM logic."}), + "legacy_unload_after_run": ("BOOLEAN", {"default": False, "tooltip": "Legacy workflow compatibility only. Model unloading is controlled by keep_model_loaded."}), + "n_ubatch": ("INT", {"default": 512, "min": 32, "max": 32768, "step": 32, "tooltip": "Physical batch size. Keep at or below n_batch. Lower values can help throughput or memory stability depending on the backend."}), "n_threads": ("INT", {"default": 0, "min": 0, "max": 256, "tooltip": "CPU generation threads. Set 0 to let llama.cpp choose."}), "n_threads_batch": ("INT", {"default": 0, "min": 0, "max": 256, "tooltip": "CPU prompt/batch threads. Set 0 to let llama.cpp choose."}), "flash_attn": ("BOOLEAN", {"default": True, "tooltip": "Enable flash attention when the installed llama.cpp backend supports it."}), "offload_kqv": ("BOOLEAN", {"default": True, "tooltip": "Keep K/Q/V and KV-cache related work on GPU when supported."}), "ctx_checkpoints": ("INT", {"default": 0, "min": 0, "max": 32, "tooltip": "Checkpoint count for multimodal context handling. JamePeng docs recommend 0 for single-turn ComfyUI-style multimodal runs."}), - "keep_model_loaded": ("BOOLEAN", {"default": False}), - "seed": ("INT", {"default": 1, "min": 1, "max": 2**32 - 1}), "keep_last_prompt": ("BOOLEAN", {"default": False, "tooltip": "Keep the last generated prompt instead of creating a new one"}), "stream_tokens_to_terminal": ("BOOLEAN", {"default": False, "tooltip": "Print every generated token live to the ComfyUI terminal/console"}), "enable_thinking": ("BOOLEAN", {"default": True, "tooltip": "Enable model reasoning/thinking when the backend supports it: True=allow thinking, False=force direct answer. Even when enabled, easy prompts may still get a direct answer, and this node automatically disables thinking when there is not enough output budget left for useful reasoning. For non-Qwen GGUF models this is advisory."}), @@ -1542,18 +1544,20 @@ class ThinkingLLM_QwenVL_GGUF_Advanced(QwenVLGGUFBase): frame_count, ctx, n_batch, - n_ubatch, gpu_layers, image_max_tokens, top_k, pool_size, + keep_model_loaded, + seed, + legacy_seed_mode, + legacy_unload_after_run, + n_ubatch, n_threads, n_threads_batch, flash_attn, offload_kqv, ctx_checkpoints, - keep_model_loaded, - seed, keep_last_prompt, image=None, video=None, @@ -1562,6 +1566,8 @@ class ThinkingLLM_QwenVL_GGUF_Advanced(QwenVLGGUFBase): stream_tokens_to_terminal=False, enable_thinking=True, ): + _ = legacy_seed_mode + _ = legacy_unload_after_run result = self.run( model_name=model_name, preset_prompt=preset_prompt, diff --git a/pyproject.toml b/pyproject.toml index ef55b31..0ade910 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "ComfyUI-ThinkingLLM" description = "A multimodal ComfyUI AI node with Qwen3.5, Qwen3-VL, Qwen2.5-VL, Qwen3, and Gemma 4 integrations. Features live thinking in the terminal to see what the LLM is doing in real time." -version = "1.0.3" +version = "1.0.4" license = {file = "LICENSE"} dependencies = ["transformers>=5.2.0", "torch", "huggingface-hub", "hf_xet", "psutil", "numpy", "Pillow", "opencv-python", "bitsandbytes", "accelerate", "sentencepiece", "tiktoken", "llama-cpp-python"] diff --git a/tests/test_new_features.py b/tests/test_new_features.py index 5bb632e..605bfcf 100644 --- a/tests/test_new_features.py +++ b/tests/test_new_features.py @@ -9,6 +9,7 @@ plain shell without ML dependencies. import ast import importlib.util +import inspect import json import os import re @@ -428,6 +429,68 @@ class TestLegacyNodeNameCompatibility(unittest.TestCase): self.assertIn(node_type, resolved_types) +class TestGGUFAdvancedWorkflowCompatibility(unittest.TestCase): + LEGACY_REQUIRED_PREFIX = [ + "model_name", + "device", + "preset_prompt", + "custom_prompt", + "max_tokens", + "temperature", + "top_p", + "repetition_penalty", + "frame_count", + "ctx", + "n_batch", + "gpu_layers", + "image_max_tokens", + "top_k", + "pool_size", + "keep_model_loaded", + "seed", + ] + + def _load_gguf_advanced_class(self): + package = load_thinkingllm_loader_subset(["AILab_QwenVL_GGUF.py"]) + return package.NODE_CLASS_MAPPINGS["ThinkingLLM_QwenVL_GGUF_Advanced"] + + def test_gguf_advanced_required_widgets_preserve_legacy_order(self): + node_cls = self._load_gguf_advanced_class() + required_keys = list(node_cls.INPUT_TYPES()["required"].keys()) + + self.assertEqual( + required_keys[: len(self.LEGACY_REQUIRED_PREFIX)], + self.LEGACY_REQUIRED_PREFIX, + ) + self.assertEqual( + required_keys[len(self.LEGACY_REQUIRED_PREFIX): len(self.LEGACY_REQUIRED_PREFIX) + 2], + ["legacy_seed_mode", "legacy_unload_after_run"], + ) + self.assertGreater(required_keys.index("n_ubatch"), required_keys.index("legacy_unload_after_run")) + + def test_gguf_advanced_process_signature_matches_required_widget_order(self): + node_cls = self._load_gguf_advanced_class() + signature = inspect.signature(node_cls.process) + parameter_names = [ + name + for name, parameter in signature.parameters.items() + if name != "self" and parameter.kind in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) + ] + + expected_prefix = self.LEGACY_REQUIRED_PREFIX + [ + "legacy_seed_mode", + "legacy_unload_after_run", + "n_ubatch", + "n_threads", + "n_threads_batch", + "flash_attn", + "offload_kqv", + "ctx_checkpoints", + "keep_last_prompt", + ] + self.assertEqual(parameter_names[: len(expected_prefix)], expected_prefix) + + class _GlobalCheck(ast.NodeVisitor): def __init__(self): self.found = False diff --git a/update.md b/update.md index 0902a17..767691f 100644 --- a/update.md +++ b/update.md @@ -1,5 +1,15 @@ # ComfyUI-QwenVL Update Log +## Version 1.0.4 (2026/05/16) — GGUF Workflow Compatibility Fix + +### Fixed +- Restored legacy widget ordering compatibility for `ThinkingLLM_QwenVL_GGUF_Advanced` so older saved workflows no longer shift values into the wrong advanced GGUF fields. +- Added compatibility shims for deprecated legacy fields (`seed_mode` and `unload_after_run`) so old workflows deserialize cleanly without affecting current runtime behavior. +- Added regression tests that lock the advanced GGUF widget order and `process()` signature alignment. + +### Validation +- Local regression suite passes: `python tests/test_new_features.py` + ## Version 2.2.5-local-locked (2026/05/14) — Complete Feature Parity ### 🎯 All 6 Nodes Now Have: