This commit is contained in:
dseditor
2025-12-05 13:58:09 +08:00
parent 7e6f2430f7
commit e0c5d828f5
2 changed files with 159 additions and 33 deletions
+9
View File
@@ -0,0 +1,9 @@
在GGUF節點中,假使使用者沒有GGUF模型,提供建議模型下載功能
在模型清單中,除了資料夾搜尋,新增幾個建議模型項目,並標註為Download,執行工作流會自動下載,之後會出現在clip資料夾下
Z-Image:https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q5_K_M.gguf
Z-Image(Abliterated):https://huggingface.co/Mungert/Qwen3-4B-abliterated-GGUF/resolve/main/Qwen3-4B-abliterated-q4_k_m.gguf
Qwen:https://huggingface.co/unsloth/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/Qwen2.5-VL-7B-Instruct-Q4_K_M.gguf
Qwen(Abliterated):https://huggingface.co/mradermacher/Qwen2.5-VL-7B-Instruct-abliterated-GGUF/resolve/main/Qwen2.5-VL-7B-Instruct-abliterated.Q4_K_M.gguf
假使有同檔名的模型,選取這些項目後不會觸發下載
假使沒有mmproj可選擇,則建議如下
mmproj:https://huggingface.co/unsloth/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/mmproj-F16.gguf
+150 -33
View File
@@ -9,8 +9,21 @@ import base64
import io
import numpy as np
import folder_paths
import urllib.request
from typing import Optional, Tuple, List
# Suggested models for download
SUGGESTED_MODELS = {
"Download: Z-Image": "https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q5_K_M.gguf",
"Download: Z-Image (Abliterated)": "https://huggingface.co/Mungert/Qwen3-4B-abliterated-GGUF/resolve/main/Qwen3-4B-abliterated-q4_k_m.gguf",
"Download: Qwen": "https://huggingface.co/unsloth/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/Qwen2.5-VL-7B-Instruct-Q4_K_M.gguf",
"Download: Qwen (Abliterated)": "https://huggingface.co/mradermacher/Qwen2.5-VL-7B-Instruct-abliterated-GGUF/resolve/main/Qwen2.5-VL-7B-Instruct-abliterated.Q4_K_M.gguf",
}
SUGGESTED_MMPROJ = {
"Download: mmproj": "https://huggingface.co/unsloth/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/mmproj-F16.gguf",
}
class GGUFInference:
"""
GGUF Model Inference Node with llama-cpp-python
@@ -35,6 +48,28 @@ class GGUFInference:
self.llama_cpp_available = False
print("llama-cpp-python is not installed")
@classmethod
def _download_file(cls, url: str, destination: str) -> bool:
"""Download file from URL with progress reporting"""
try:
print(f"Downloading: {url}")
print(f"Destination: {destination}")
def report_progress(block_num, block_size, total_size):
downloaded = block_num * block_size
if total_size > 0:
percent = min(downloaded * 100 / total_size, 100)
downloaded_mb = downloaded / (1024 * 1024)
total_mb = total_size / (1024 * 1024)
print(f"Progress: {percent:.1f}% ({downloaded_mb:.1f}MB / {total_mb:.1f}MB)", end='\r')
urllib.request.urlretrieve(url, destination, reporthook=report_progress)
print(f"\nDownload completed: {os.path.basename(destination)}")
return True
except Exception as e:
print(f"\nDownload failed: {e}")
return False
@classmethod
def _get_gguf_files(cls):
"""Get all GGUF files from text_encoders and clip folders"""
@@ -66,10 +101,15 @@ class GGUFInference:
except:
pass
if not gguf_files:
return ["No GGUF files found"]
# Add suggested models to the list
suggested_models = list(SUGGESTED_MODELS.keys())
return sorted(gguf_files)
if not gguf_files:
# If no local models, only show suggested models
return suggested_models
# Return local models first, then suggested models
return sorted(gguf_files) + suggested_models
@classmethod
def _get_mmproj_files(cls):
@@ -102,10 +142,15 @@ class GGUFInference:
except:
pass
if not mmproj_files:
return ["No mmproj files"]
# Add suggested mmproj to the list
suggested_mmproj = list(SUGGESTED_MMPROJ.keys())
return sorted(mmproj_files)
if not mmproj_files:
# If no local mmproj files, show "No mmproj files" and suggested option
return ["No mmproj files"] + suggested_mmproj
# Return local mmproj files first, then suggested option
return sorted(mmproj_files) + suggested_mmproj
@classmethod
def _get_prompt_templates(cls):
@@ -447,23 +492,54 @@ class GGUFInference:
print(error_msg)
return (error_msg,)
# Get full path from model name
gguf_files = self._get_gguf_files()
model_path = None
for full_path in gguf_files:
if os.path.basename(full_path) == model:
model_path = full_path
break
# Check if model is a suggested download
if model in SUGGESTED_MODELS:
download_url = SUGGESTED_MODELS[model]
filename = os.path.basename(download_url)
if model_path is None or model_path == "No GGUF files found":
error_msg = f"Error: Model not found: {model}\nPlease place GGUF files in text_encoders or clip folder."
print(error_msg)
return (error_msg,)
# Get clip folder path for downloading
try:
clip_paths = folder_paths.get_folder_paths("clip")
if clip_paths and len(clip_paths) > 0:
download_dir = clip_paths[0]
else:
error_msg = "Error: Cannot find clip folder for downloading model."
print(error_msg)
return (error_msg,)
except:
error_msg = "Error: Cannot access clip folder for downloading model."
print(error_msg)
return (error_msg,)
if not os.path.exists(model_path):
error_msg = f"Error: Model file does not exist: {model_path}"
print(error_msg)
return (error_msg,)
model_path = os.path.join(download_dir, filename)
# Check if file already exists
if os.path.exists(model_path):
print(f"Model already exists: {filename}")
else:
print(f"Downloading suggested model: {model}")
if not self._download_file(download_url, model_path):
error_msg = f"Error: Failed to download model from {download_url}"
print(error_msg)
return (error_msg,)
else:
# Get full path from model name
gguf_files = self._get_gguf_files()
model_path = None
for full_path in gguf_files:
if os.path.basename(full_path) == model:
model_path = full_path
break
if model_path is None:
error_msg = f"Error: Model not found: {model}\nPlease place GGUF files in text_encoders or clip folder."
print(error_msg)
return (error_msg,)
if not os.path.exists(model_path):
error_msg = f"Error: Model file does not exist: {model_path}"
print(error_msg)
return (error_msg,)
# Check if this is a vision model
is_vision_model = self._is_vision_model(model_path)
@@ -486,19 +562,60 @@ class GGUFInference:
print("=" * 70)
enable_vision = False
else:
# Find full path for mmproj from text_encoders and clip folders
mmproj_files = self._get_mmproj_files()
for full_path in mmproj_files:
if os.path.basename(full_path) == mmproj_file:
mmproj_path = full_path
break
# Check if mmproj is a suggested download
if mmproj_file in SUGGESTED_MMPROJ:
download_url = SUGGESTED_MMPROJ[mmproj_file]
filename = os.path.basename(download_url)
if mmproj_path is None or not os.path.exists(mmproj_path):
print("=" * 70)
print(f"WARNING: mmproj file not found: {mmproj_file}")
print("Falling back to text-only mode.")
print("=" * 70)
enable_vision = False
# Get clip folder path for downloading
try:
clip_paths = folder_paths.get_folder_paths("clip")
if clip_paths and len(clip_paths) > 0:
download_dir = clip_paths[0]
else:
print("=" * 70)
print("WARNING: Cannot find clip folder for downloading mmproj.")
print("Falling back to text-only mode.")
print("=" * 70)
enable_vision = False
mmproj_path = None
except:
print("=" * 70)
print("WARNING: Cannot access clip folder for downloading mmproj.")
print("Falling back to text-only mode.")
print("=" * 70)
enable_vision = False
mmproj_path = None
if enable_vision:
mmproj_path = os.path.join(download_dir, filename)
# Check if file already exists
if os.path.exists(mmproj_path):
print(f"mmproj already exists: {filename}")
else:
print(f"Downloading suggested mmproj: {mmproj_file}")
if not self._download_file(download_url, mmproj_path):
print("=" * 70)
print(f"WARNING: Failed to download mmproj from {download_url}")
print("Falling back to text-only mode.")
print("=" * 70)
enable_vision = False
mmproj_path = None
else:
# Find full path for mmproj from text_encoders and clip folders
mmproj_files = self._get_mmproj_files()
for full_path in mmproj_files:
if os.path.basename(full_path) == mmproj_file:
mmproj_path = full_path
break
if mmproj_path is None or not os.path.exists(mmproj_path):
print("=" * 70)
print(f"WARNING: mmproj file not found: {mmproj_file}")
print("Falling back to text-only mode.")
print("=" * 70)
enable_vision = False
# Load model
if not self._load_model(model_path, enable_vision, mmproj_path):