Add model recommendation guides and Hauhau GGUF support
This commit is contained in:
+4
-1
@@ -64,7 +64,10 @@ try:
|
||||
sageattn_qk_int8_pv_fp8_cuda_sm90,
|
||||
)
|
||||
SAGE_ATTENTION_AVAILABLE = True
|
||||
except ImportError:
|
||||
except Exception:
|
||||
sageattn_qk_int8_pv_fp16_cuda = None
|
||||
sageattn_qk_int8_pv_fp8_cuda = None
|
||||
sageattn_qk_int8_pv_fp8_cuda_sm90 = None
|
||||
SAGE_ATTENTION_AVAILABLE = False
|
||||
|
||||
# Global cache for generated prompts
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"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.",
|
||||
"author": "goodguy1963",
|
||||
"version": "2.3.3",
|
||||
"version": "2.3.4",
|
||||
"url": "https://github.com/goodguy1963/ComfyUI-ThinkingLLM",
|
||||
"category": "image"
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
"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.",
|
||||
"category": "image",
|
||||
"tags": ["vision", "language", "multimodal", "thinkingllm", "qwen", "gguf", "flash_attention", "performance", "wan2.2", "video", "i2v", "t2v", "storyboard", "caching", "fork", "gpl-3.0"],
|
||||
"version": "2.3.3"
|
||||
"version": "2.3.4"
|
||||
}
|
||||
|
||||
@@ -189,6 +189,27 @@
|
||||
"context_length": 131072
|
||||
}
|
||||
},
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-GGUF [Q2:11GB|Q3:15GB|Q4:20GB|Q5:28GB|Q6:31GB|Q8:44GB|VRAM:~24GB+]": {
|
||||
"author": "HauhauCS",
|
||||
"repo_name": "Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive",
|
||||
"repo_id": "HauhauCS/Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive",
|
||||
"model_files": [
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q8_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q5_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ4_NL.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q3_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ3_M.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q2_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ2_M.gguf"
|
||||
],
|
||||
"defaults": {
|
||||
"context_length": 131072
|
||||
}
|
||||
},
|
||||
"Gemma-4-E2B-it-GGUF [Q4:2GB|Q8:3.5GB|VRAM:~3GB]": {
|
||||
"author": "ggml-org",
|
||||
"repo_name": "gemma-4-E2B-it-GGUF",
|
||||
@@ -333,6 +354,33 @@
|
||||
"pool_size": 4194304
|
||||
}
|
||||
},
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive [Q2:11GB|Q3:15GB|Q4:20GB|Q5:28GB|Q6:31GB|Q8:44GB|VRAM:~24GB+]": {
|
||||
"author": "HauhauCS",
|
||||
"repo_name": "Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive",
|
||||
"repo_id": "HauhauCS/Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive",
|
||||
"mmproj_file": "mmproj-Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-f16.gguf",
|
||||
"model_files": [
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q8_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q5_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ4_NL.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q3_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ3_M.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q2_K_P.gguf",
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ2_M.gguf"
|
||||
],
|
||||
"defaults": {
|
||||
"context_length": 131072,
|
||||
"image_max_tokens": 4096,
|
||||
"n_batch": 512,
|
||||
"gpu_layers": -1,
|
||||
"top_k": 20,
|
||||
"pool_size": 4194304
|
||||
}
|
||||
},
|
||||
"Qwen3.5-4B [Q4:2.4GB|Q6:3.2GB|Q8:4.2GB|BF16:8GB|VRAM:~4GB]": {
|
||||
"author": "unsloth",
|
||||
"repo_name": "Qwen3.5-4B-GGUF",
|
||||
|
||||
+1
-1
@@ -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 = "2.3.3"
|
||||
version = "2.3.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"]
|
||||
|
||||
|
||||
@@ -272,6 +272,38 @@ def iter_workflow_types(path: Path) -> list[str]:
|
||||
]
|
||||
|
||||
|
||||
def load_model_recommendations() -> dict:
|
||||
return json.loads((PKG / "web" / "model_recommendations.json").read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def matches_recommendation_rule(model_name: str, rules: list[dict]) -> bool:
|
||||
lowered = model_name.lower()
|
||||
ordered = sorted(rules, key=lambda rule: rule.get("priority", 0), reverse=True)
|
||||
for rule in ordered:
|
||||
include = [token.lower() for token in rule.get("match_any", [])]
|
||||
exclude = [token.lower() for token in rule.get("exclude_any", [])]
|
||||
if any(token in lowered for token in include) and not any(token in lowered for token in exclude):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def iter_catalog_display_names() -> list[str]:
|
||||
names: list[str] = []
|
||||
hf_payload = json.loads((PKG / "hf_models.json").read_text(encoding="utf-8"))
|
||||
for section in ("hf_vl_models", "hf_text_models"):
|
||||
names.extend(list((hf_payload.get(section) or {}).keys()))
|
||||
|
||||
gguf_payload = json.loads((PKG / "gguf_models.json").read_text(encoding="utf-8"))
|
||||
for section in ("Qwen_model", "qwenVL_model"):
|
||||
entries = gguf_payload.get(section) or {}
|
||||
for repo_key, repo in entries.items():
|
||||
if not isinstance(repo, dict):
|
||||
continue
|
||||
for model_file in repo.get("model_files") or []:
|
||||
names.append(str(Path(model_file).name))
|
||||
return names
|
||||
|
||||
|
||||
class TestTerminalDisplayHelper(unittest.TestCase):
|
||||
def test_helper_file_exists(self):
|
||||
self.assertTrue((PKG / "AILab_StreamDisplay.py").exists())
|
||||
@@ -318,8 +350,44 @@ class TestBufferedStreamingIntegration(unittest.TestCase):
|
||||
def test_gguf_prompt_enhancer_has_no_off_toggle_text_stream_fallback(self):
|
||||
source = read_source("AILab_QwenVL_GGUF_PromptEnhancer.py")
|
||||
self.assertNotIn("_maybe_emit_prompt_compact_progress", source)
|
||||
|
||||
|
||||
class TestModelRecommendations(unittest.TestCase):
|
||||
def test_recommendation_rules_have_sources(self):
|
||||
payload = load_model_recommendations()
|
||||
rules = payload.get("rules") or []
|
||||
self.assertTrue(rules, "model_recommendations.json should define at least one rule")
|
||||
for rule in rules:
|
||||
with self.subTest(rule=rule.get("id")):
|
||||
self.assertTrue(rule.get("sources"), "each recommendation rule should record its research source")
|
||||
|
||||
def test_recommendations_cover_all_catalog_models(self):
|
||||
payload = load_model_recommendations()
|
||||
rules = payload.get("rules") or []
|
||||
uncovered = [name for name in iter_catalog_display_names() if not matches_recommendation_rule(name, rules)]
|
||||
self.assertEqual([], uncovered)
|
||||
|
||||
def test_qwen36_hauhau_exists_in_text_and_vision_catalogs(self):
|
||||
payload = json.loads((PKG / "gguf_models.json").read_text(encoding="utf-8"))
|
||||
text_catalog = payload.get("Qwen_model") or {}
|
||||
vision_catalog = payload.get("qwenVL_model") or {}
|
||||
|
||||
text_key = next((key for key in text_catalog if "Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive" in key), None)
|
||||
vision_key = next((key for key in vision_catalog if "Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive" in key), None)
|
||||
|
||||
self.assertIsNotNone(text_key)
|
||||
self.assertIsNotNone(vision_key)
|
||||
self.assertIn("mmproj-Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-f16.gguf", vision_catalog[vision_key]["mmproj_file"])
|
||||
self.assertGreaterEqual(len(text_catalog[text_key].get("model_files") or []), 11)
|
||||
self.assertGreaterEqual(len(vision_catalog[vision_key].get("model_files") or []), 11)
|
||||
|
||||
def test_appearance_js_loads_recommendation_widget(self):
|
||||
source = read_source("web/js/appearance.js")
|
||||
self.assertIn("model_recommendations.json", source)
|
||||
self.assertIn("ComfyWidgets", source)
|
||||
self.assertIn("recommended_settings", source)
|
||||
self.assertNotIn("Prompt enhancer compact terminal progress is active", source)
|
||||
self.assertIn("_maybe_emit_prompt_background_heartbeat", source)
|
||||
self.assertIn("queueRecommendationRefresh", source)
|
||||
|
||||
def test_gguf_prompt_enhancer_caps_context_and_safe_constructs_llama(self):
|
||||
source = read_source("AILab_QwenVL_GGUF_PromptEnhancer.py")
|
||||
|
||||
+234
-4
@@ -1,4 +1,5 @@
|
||||
import { app } from "/scripts/app.js";
|
||||
import { ComfyWidgets } from "/scripts/widgets.js";
|
||||
|
||||
const COLOR_THEMES = {
|
||||
QwenVL: { nodeColor: "#28403f", nodeBgColor: "#374539", width: 340 },
|
||||
@@ -8,15 +9,12 @@ const COLOR_THEMES = {
|
||||
};
|
||||
|
||||
const NODE_COLORS = {
|
||||
// QwenVL nodes
|
||||
"AILab_QwenVL": "QwenVL",
|
||||
"AILab_QwenVL_Advanced": "QwenVL",
|
||||
"AILab_QwenVL_PromptEnhancer": "Enhancer",
|
||||
"AILab_QwenVL_GGUF": "QwenVLGGUF",
|
||||
"AILab_QwenVL_GGUF_Advanced": "QwenVLGGUF",
|
||||
"AILab_QwenVL_GGUF_PromptEnhancer": "Enhancer",
|
||||
|
||||
// Tools
|
||||
"AILab_QwenVL_PromptLibrary": "Tools",
|
||||
};
|
||||
|
||||
@@ -46,6 +44,31 @@ const GGUF_ADVANCED_INTERNAL_WIDGETS = new Set([
|
||||
"ctx_checkpoints",
|
||||
]);
|
||||
|
||||
const RECOMMENDATIONS_URL = new URL("../model_recommendations.json", import.meta.url);
|
||||
const RECOMMENDATION_WIDGET_NAME = "recommended_settings";
|
||||
const RECOMMENDATION_PLACEHOLDER = "Select a model to see provider/community recommended settings. This note never changes your saved widget values.";
|
||||
const RECOMMENDATION_FIELDS = ["temperature", "top_p", "top_k", "max_tokens", "ctx_min", "n_batch", "image_max_tokens"];
|
||||
|
||||
let recommendationsPromise = null;
|
||||
|
||||
function loadRecommendations() {
|
||||
if (!recommendationsPromise) {
|
||||
recommendationsPromise = fetch(RECOMMENDATIONS_URL)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((payload) => Array.isArray(payload?.rules) ? payload.rules : [])
|
||||
.catch((error) => {
|
||||
console.warn("[ThinkingLLM] Failed to load model recommendations", error);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
return recommendationsPromise;
|
||||
}
|
||||
|
||||
function setNodeColors(node, theme) {
|
||||
if (!theme) { return; }
|
||||
if (theme.nodeColor) {
|
||||
@@ -103,17 +126,224 @@ function clearHfTokenAfterExecution(node) {
|
||||
};
|
||||
}
|
||||
|
||||
function recommendationScope(node) {
|
||||
if ([
|
||||
"AILab_QwenVL_GGUF",
|
||||
"AILab_QwenVL_GGUF_Advanced",
|
||||
"ThinkingLLM_QwenVL_GGUF",
|
||||
"ThinkingLLM_QwenVL_GGUF_Advanced",
|
||||
"AILab_QwenVL_GGUF_PromptEnhancer",
|
||||
"ThinkingLLM_QwenVL_GGUF_PromptEnhancer",
|
||||
].includes(node.comfyClass)) {
|
||||
return "gguf";
|
||||
}
|
||||
return "hf";
|
||||
}
|
||||
|
||||
function matchRecommendationRule(modelName, rules) {
|
||||
const lowered = String(modelName || "").toLowerCase();
|
||||
if (!lowered) {
|
||||
return null;
|
||||
}
|
||||
const ordered = [...(rules || [])].sort((left, right) => (right.priority || 0) - (left.priority || 0));
|
||||
for (const rule of ordered) {
|
||||
const include = Array.isArray(rule?.match_any) ? rule.match_any : [];
|
||||
const exclude = Array.isArray(rule?.exclude_any) ? rule.exclude_any : [];
|
||||
const matches = include.some((token) => lowered.includes(String(token).toLowerCase()));
|
||||
const blocked = exclude.some((token) => lowered.includes(String(token).toLowerCase()));
|
||||
if (matches && !blocked) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasWidget(node, widgetName) {
|
||||
return Boolean(findWidget(node, widgetName));
|
||||
}
|
||||
|
||||
function formatSetting(fieldName, value) {
|
||||
if (typeof value !== "number") {
|
||||
return null;
|
||||
}
|
||||
if (fieldName === "ctx_min") {
|
||||
return `ctx>=${value}`;
|
||||
}
|
||||
return `${fieldName}=${value}`;
|
||||
}
|
||||
|
||||
function buildModeSummary(node, settings) {
|
||||
const parts = [];
|
||||
for (const fieldName of RECOMMENDATION_FIELDS) {
|
||||
if (!(fieldName in (settings || {}))) {
|
||||
continue;
|
||||
}
|
||||
if (fieldName === "ctx_min") {
|
||||
if (!hasWidget(node, "ctx")) {
|
||||
continue;
|
||||
}
|
||||
} else if (fieldName === "top_k") {
|
||||
if (!hasWidget(node, "top_k")) {
|
||||
continue;
|
||||
}
|
||||
} else if (!hasWidget(node, fieldName)) {
|
||||
continue;
|
||||
}
|
||||
const formatted = formatSetting(fieldName, settings[fieldName]);
|
||||
if (formatted) {
|
||||
parts.push(formatted);
|
||||
}
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
function buildRecommendationText(node, rules) {
|
||||
const modelName = String(findWidget(node, "model_name")?.value || "").trim();
|
||||
if (!modelName || modelName.startsWith("(no ")) {
|
||||
return RECOMMENDATION_PLACEHOLDER;
|
||||
}
|
||||
|
||||
const rule = matchRecommendationRule(modelName, rules);
|
||||
if (!rule) {
|
||||
return [
|
||||
"Manual settings guide",
|
||||
`No curated recommendation is bundled yet for ${modelName}.`,
|
||||
"Your current widget values stay exactly as you set them.",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
const thinkingEnabled = Boolean(findWidget(node, "enable_thinking")?.value);
|
||||
const activeSettings = thinkingEnabled ? rule.thinking_on : rule.thinking_off;
|
||||
const alternateSettings = thinkingEnabled ? rule.thinking_off : rule.thinking_on;
|
||||
const activeSummary = buildModeSummary(node, activeSettings);
|
||||
const alternateSummary = buildModeSummary(node, alternateSettings);
|
||||
const scope = recommendationScope(node);
|
||||
const lines = [
|
||||
rule.family_label || "Manual settings guide",
|
||||
`Current toggle: ${thinkingEnabled ? "thinking ON" : "thinking OFF"}`,
|
||||
];
|
||||
|
||||
const supportNote = rule.support_notes?.[scope];
|
||||
if (supportNote) {
|
||||
lines.push(`Toggle: ${supportNote}`);
|
||||
}
|
||||
lines.push(`Thinking ON: ${(thinkingEnabled ? activeSummary : alternateSummary).join(" | ") || "use the provider defaults"}`);
|
||||
lines.push(`Thinking OFF: ${(thinkingEnabled ? alternateSummary : activeSummary).join(" | ") || "use the provider defaults"}`);
|
||||
|
||||
const softSwitch = rule.support_notes?.soft_switch;
|
||||
if (softSwitch) {
|
||||
lines.push(`Note: ${softSwitch}`);
|
||||
}
|
||||
if (Array.isArray(rule.notes) && rule.notes.length) {
|
||||
lines.push(`Extra: ${rule.notes[0]}`);
|
||||
}
|
||||
if (Array.isArray(rule.sources) && rule.sources.length) {
|
||||
lines.push(`Sources: ${rule.sources.join(" + ")}`);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function ensureRecommendationWidget(node) {
|
||||
if (!THINKINGLLM_NODE_NAMES.has(node.comfyClass)) {
|
||||
return null;
|
||||
}
|
||||
let widget = findWidget(node, RECOMMENDATION_WIDGET_NAME);
|
||||
if (widget) {
|
||||
return widget;
|
||||
}
|
||||
widget = ComfyWidgets.STRING(node, RECOMMENDATION_WIDGET_NAME, ["STRING", { multiline: true }], app).widget;
|
||||
if (widget?.inputEl) {
|
||||
widget.inputEl.readOnly = true;
|
||||
widget.inputEl.style.opacity = 0.78;
|
||||
widget.inputEl.style.fontSize = "12px";
|
||||
widget.inputEl.style.lineHeight = "1.35";
|
||||
widget.inputEl.rows = 6;
|
||||
}
|
||||
widget.options = { ...(widget.options || {}), serialize: false };
|
||||
widget.serializeValue = async () => undefined;
|
||||
widget.value = RECOMMENDATION_PLACEHOLDER;
|
||||
return widget;
|
||||
}
|
||||
|
||||
function resizeNode(node) {
|
||||
requestAnimationFrame(() => {
|
||||
const size = node.computeSize();
|
||||
if (size[0] < node.size[0]) {
|
||||
size[0] = node.size[0];
|
||||
}
|
||||
if (size[1] < node.size[1]) {
|
||||
size[1] = node.size[1];
|
||||
}
|
||||
node.setSize(size);
|
||||
app.graph?.setDirtyCanvas?.(true, false);
|
||||
});
|
||||
}
|
||||
|
||||
function updateRecommendationWidget(node) {
|
||||
const widget = ensureRecommendationWidget(node);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
loadRecommendations().then((rules) => {
|
||||
widget.value = buildRecommendationText(node, rules);
|
||||
resizeNode(node);
|
||||
});
|
||||
}
|
||||
|
||||
function queueRecommendationRefresh(node) {
|
||||
if (!node || node._thinkingllmRecommendationQueued) {
|
||||
return;
|
||||
}
|
||||
node._thinkingllmRecommendationQueued = true;
|
||||
requestAnimationFrame(() => {
|
||||
node._thinkingllmRecommendationQueued = false;
|
||||
updateRecommendationWidget(node);
|
||||
});
|
||||
}
|
||||
|
||||
function hookRecommendationUpdates(node) {
|
||||
if (!THINKINGLLM_NODE_NAMES.has(node.comfyClass) || node._thinkingllmRecommendationHooked) {
|
||||
return;
|
||||
}
|
||||
node._thinkingllmRecommendationHooked = true;
|
||||
ensureRecommendationWidget(node);
|
||||
|
||||
for (const widgetName of ["model_name", "enable_thinking"]) {
|
||||
const target = findWidget(node, widgetName);
|
||||
if (!target || target._thinkingllmRecommendationCallbackHooked) {
|
||||
continue;
|
||||
}
|
||||
target._thinkingllmRecommendationCallbackHooked = true;
|
||||
const originalCallback = target.callback;
|
||||
target.callback = function (...args) {
|
||||
const result = originalCallback?.apply(this, args);
|
||||
queueRecommendationRefresh(node);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
const originalConfigure = node.onConfigure;
|
||||
node.onConfigure = function (...args) {
|
||||
const result = originalConfigure?.apply(this, args);
|
||||
queueRecommendationRefresh(this);
|
||||
return result;
|
||||
};
|
||||
|
||||
queueRecommendationRefresh(node);
|
||||
}
|
||||
|
||||
const ext = {
|
||||
name: "QwenVL.appearance",
|
||||
|
||||
nodeCreated(node) {
|
||||
const nclass = node.comfyClass;
|
||||
if (NODE_COLORS.hasOwnProperty(nclass)) {
|
||||
let colorKey = NODE_COLORS[nclass];
|
||||
const colorKey = NODE_COLORS[nclass];
|
||||
const theme = COLOR_THEMES[colorKey];
|
||||
setNodeColors(node, theme);
|
||||
}
|
||||
hideGgufAdvancedInternals(node);
|
||||
hookRecommendationUpdates(node);
|
||||
clearHfTokenAfterExecution(node);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
{
|
||||
"version": 1,
|
||||
"rules": [
|
||||
{
|
||||
"id": "qwen36_hauhau_multimodal",
|
||||
"priority": 100,
|
||||
"match_any": [
|
||||
"Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive"
|
||||
],
|
||||
"family_label": "Qwen3.6 35B A3B HauhauCS aggressive",
|
||||
"sources": [
|
||||
"HauhauCS model card",
|
||||
"Qwen3.5 official model card"
|
||||
],
|
||||
"support_notes": {
|
||||
"hf": "Not in the HF lineup here. Use the GGUF nodes for this model.",
|
||||
"gguf": "Hard toggle via enable_thinking. Pair the vision node with the matching mmproj file.",
|
||||
"soft_switch": "No official /think or /no_think soft switch for this family."
|
||||
},
|
||||
"thinking_on": {
|
||||
"temperature": 1.0,
|
||||
"top_p": 0.95,
|
||||
"top_k": 20,
|
||||
"max_tokens": 32768,
|
||||
"ctx_min": 131072
|
||||
},
|
||||
"thinking_off": {
|
||||
"temperature": 0.7,
|
||||
"top_p": 0.8,
|
||||
"top_k": 20,
|
||||
"max_tokens": 32768,
|
||||
"ctx_min": 131072
|
||||
},
|
||||
"notes": [
|
||||
"Provider says keep at least 128K context to preserve thinking quality.",
|
||||
"Q4_K_P is the best quality-size starting point; move to Q6_K_P or Q8_K_P when VRAM allows.",
|
||||
"IQ quants are fallback options when you cannot fit Q4_K_M or Q4_K_P."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen35_family",
|
||||
"priority": 80,
|
||||
"match_any": [
|
||||
"Qwen3.5",
|
||||
"qwen3.5"
|
||||
],
|
||||
"family_label": "Qwen3.5 family",
|
||||
"sources": [
|
||||
"Qwen3.5 official model card"
|
||||
],
|
||||
"support_notes": {
|
||||
"hf": "Hard toggle via enable_thinking when the backend honors the chat template.",
|
||||
"gguf": "Hard toggle via enable_thinking in the Qwen chat template.",
|
||||
"soft_switch": "No official /think or /no_think soft switch for Qwen3.5."
|
||||
},
|
||||
"thinking_on": {
|
||||
"temperature": 1.0,
|
||||
"top_p": 0.95,
|
||||
"top_k": 20,
|
||||
"max_tokens": 32768,
|
||||
"ctx_min": 131072
|
||||
},
|
||||
"thinking_off": {
|
||||
"temperature": 0.7,
|
||||
"top_p": 0.8,
|
||||
"top_k": 20,
|
||||
"max_tokens": 32768,
|
||||
"ctx_min": 131072
|
||||
},
|
||||
"notes": [
|
||||
"For precise coding, Qwen recommends temperature 0.6 with thinking still enabled.",
|
||||
"For hard non-thinking reasoning tasks, Qwen recommends top_p 1.0 and top_k 40 when the backend exposes them.",
|
||||
"Provider says keep at least 128K context to preserve thinking capability and use 32K+ output budget when possible."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3_family",
|
||||
"priority": 70,
|
||||
"match_any": [
|
||||
"Qwen3VL-",
|
||||
"qwen3vl-",
|
||||
"Qwen3-VL",
|
||||
"Qwen3-",
|
||||
"qwen3-"
|
||||
],
|
||||
"exclude_any": [
|
||||
"Qwen3.5",
|
||||
"qwen3.5",
|
||||
"Qwen3.6",
|
||||
"qwen3.6"
|
||||
],
|
||||
"family_label": "Qwen3 family",
|
||||
"sources": [
|
||||
"Qwen3 official model card",
|
||||
"Qwen Transformers docs"
|
||||
],
|
||||
"support_notes": {
|
||||
"hf": "Hard toggle via enable_thinking, with /think and /no_think available as soft switches.",
|
||||
"gguf": "Hard toggle via enable_thinking. /think and /no_think are available only on Qwen3 chat-template builds.",
|
||||
"soft_switch": "Qwen3 officially supports /think and /no_think when thinking is enabled."
|
||||
},
|
||||
"thinking_on": {
|
||||
"temperature": 0.6,
|
||||
"top_p": 0.95,
|
||||
"top_k": 20,
|
||||
"max_tokens": 32768,
|
||||
"ctx_min": 32768
|
||||
},
|
||||
"thinking_off": {
|
||||
"temperature": 0.7,
|
||||
"top_p": 0.8,
|
||||
"top_k": 20,
|
||||
"max_tokens": 32768,
|
||||
"ctx_min": 32768
|
||||
},
|
||||
"notes": [
|
||||
"Qwen warns against greedy decoding for thinking mode because it can repeat or degrade quality.",
|
||||
"Provider recommends 32K output budget for most tasks and only enabling YaRN when you really need longer than the native 32K context.",
|
||||
"For harder reasoning benchmarks, Qwen suggests stretching output budget beyond 32K if your backend allows it."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gemma4_large_family",
|
||||
"priority": 60,
|
||||
"match_any": [
|
||||
"Gemma-4-26B",
|
||||
"gemma-4-26B",
|
||||
"Gemma-4-31B",
|
||||
"gemma-4-31B"
|
||||
],
|
||||
"family_label": "Gemma 4 large family",
|
||||
"sources": [
|
||||
"Gemma 4 official model card"
|
||||
],
|
||||
"support_notes": {
|
||||
"hf": "Gemma 4 supports configurable thinking, but current repo behavior can vary by backend.",
|
||||
"gguf": "Use the settings manually and verify behavior in RAW_TRACE because GGUF support varies by backend.",
|
||||
"soft_switch": "Gemma uses chat-template thinking controls instead of /think or /no_think."
|
||||
},
|
||||
"thinking_on": {
|
||||
"temperature": 1.0,
|
||||
"top_p": 0.95,
|
||||
"top_k": 64,
|
||||
"max_tokens": 1024,
|
||||
"ctx_min": 262144
|
||||
},
|
||||
"thinking_off": {
|
||||
"temperature": 1.0,
|
||||
"top_p": 0.95,
|
||||
"top_k": 64,
|
||||
"max_tokens": 1024,
|
||||
"ctx_min": 262144
|
||||
},
|
||||
"notes": [
|
||||
"Gemma 4 uses one standardized sampler for both thinking and non-thinking modes.",
|
||||
"Official context target is 256K on 26B A4B and 31B variants.",
|
||||
"If the backend does not expose top_k, keep temperature and top_p at the official defaults."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gemma4_small_family",
|
||||
"priority": 50,
|
||||
"match_any": [
|
||||
"Gemma-4-E2B",
|
||||
"gemma-4-E2B",
|
||||
"Gemma-4-E4B",
|
||||
"gemma-4-E4B"
|
||||
],
|
||||
"family_label": "Gemma 4 small family",
|
||||
"sources": [
|
||||
"Gemma 4 official model card"
|
||||
],
|
||||
"support_notes": {
|
||||
"hf": "Gemma 4 supports configurable thinking, but current repo behavior can vary by backend.",
|
||||
"gguf": "Use the settings manually and verify behavior in RAW_TRACE because GGUF support varies by backend.",
|
||||
"soft_switch": "Gemma uses chat-template thinking controls instead of /think or /no_think."
|
||||
},
|
||||
"thinking_on": {
|
||||
"temperature": 1.0,
|
||||
"top_p": 0.95,
|
||||
"top_k": 64,
|
||||
"max_tokens": 1024,
|
||||
"ctx_min": 131072
|
||||
},
|
||||
"thinking_off": {
|
||||
"temperature": 1.0,
|
||||
"top_p": 0.95,
|
||||
"top_k": 64,
|
||||
"max_tokens": 1024,
|
||||
"ctx_min": 131072
|
||||
},
|
||||
"notes": [
|
||||
"Gemma 4 uses one standardized sampler for both thinking and non-thinking modes.",
|
||||
"Official context target is 128K on E2B and E4B variants.",
|
||||
"Gemma recommends higher visual token budgets only for OCR or fine detail tasks; lower budgets are faster for captioning or video summaries."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user