diff --git a/AILab_MiniCPM.py b/AILab_MiniCPM.py index 1608e14..394fe74 100644 --- a/AILab_MiniCPM.py +++ b/AILab_MiniCPM.py @@ -9,11 +9,15 @@ import json import gc import sys import io +import warnings from pathlib import Path os.environ['TRANSFORMERS_VERBOSITY'] = 'error' os.environ['TOKENIZERS_PARALLELISM'] = 'false' +# Suppress transformers FutureWarnings for better user experience +warnings.filterwarnings("ignore", category=FutureWarning, module="transformers") + if torch.cuda.is_available(): torch.backends.cudnn.benchmark = True if hasattr(torch.backends, 'cuda'): @@ -45,12 +49,11 @@ class MiniCPM_Transformers_Models: self.model_checkpoint = prompt_generator_dir / Path(model_id).name if not self.model_checkpoint.exists(): - print(f"Downloading model: {model_id}") + print(f"Downloading model: {model_id} (this may take several minutes...)") from huggingface_hub import snapshot_download snapshot_download( repo_id=model_id, - local_dir=str(self.model_checkpoint), - local_dir_use_symlinks=False, + local_dir=str(self.model_checkpoint) ) self.device = torch.device("cuda" if processing_mode == "GPU" and torch.cuda.is_available() else "cpu") @@ -244,7 +247,7 @@ class AILab_MiniCPM_4_V(MiniCPM_Transformers_Base): RETURN_TYPES = ("STRING",) RETURN_NAMES = ("STRING",) FUNCTION = "generate" - CATEGORY = "🧪AILab/MiniCPM" + CATEGORY = "🧪AILab/📝MiniCPM" def generate(self, image=None, video=None, model=None, preset_prompt="Describe", custom_prompt="", device="Auto", memory_management="Keep in Memory", seed=-1): try: @@ -322,7 +325,7 @@ class AILab_MiniCPM_4_V_Advanced(MiniCPM_Transformers_Base): RETURN_TYPES = ("STRING", "STRING") RETURN_NAMES = ("PROMPT", "STRING") FUNCTION = "generate" - CATEGORY = "🧪AILab/MiniCPM" + CATEGORY = "🧪AILab/📝MiniCPM" def generate(self, image=None, video=None, model=None, preset_prompt="Describe", custom_prompt="", system_prompt="", max_new_tokens=None, temperature=None, top_p=None, top_k=None, repetition_penalty=None, video_max_num_frames=64, video_max_slice_nums=2, device="Auto", memory_management="Keep in Memory", seed=-1): try: diff --git a/AILab_MiniCPM_GGUF.py b/AILab_MiniCPM_GGUF.py index e883186..d54d521 100644 --- a/AILab_MiniCPM_GGUF.py +++ b/AILab_MiniCPM_GGUF.py @@ -59,27 +59,56 @@ class MiniCPM_GGUF_Models: raise ValueError("Invalid model path") repo_path, filename = model.rsplit("/", 1) - model_path = llm_models_dir / filename - if not model_path.exists(): - print(f"Downloading model: {filename}") - model_path = Path(hf_hub_download( - repo_id=repo_path, - filename=filename, - local_dir=str(llm_models_dir), - local_dir_use_symlinks=False - )).resolve() + model_config = None + model_key = None + for key, config in GGUF_MODELS.items(): + if config["name"] == model: + model_config = config + model_key = key + break + + if not model_config: + raise ValueError(f"Model configuration not found for: {model}") - mmproj_filename = GGUF_MODELS.get("MiniCPM-V-4 (Q4_0)", {}).get("mmproj", "openbmb/MiniCPM-V-4-gguf/mmproj-model-f16.gguf") - mmproj_local = llm_models_dir / Path(mmproj_filename).name + if "download_path" in model_config: + download_subdir = llm_models_dir / model_config["download_path"] + else: + download_subdir = llm_models_dir + download_subdir.mkdir(parents=True, exist_ok=True) + + model_path = download_subdir / filename + if not model_path.exists(): + print(f"Downloading GGUF model: {filename} (large file, please wait...)") + try: + model_path = Path(hf_hub_download( + repo_id=repo_path, + filename=filename, + local_dir=str(download_subdir) + )).resolve() + except Exception as e: + print(f"GGUF model download failed: {e}") + raise + + mmproj_filename = model_config.get("mmproj") + if not mmproj_filename: + if "MiniCPM-V-4.5" in model_key or "4_5" in model: + mmproj_filename = "openbmb/MiniCPM-V-4_5-gguf/mmproj-model-f16.gguf" + else: + mmproj_filename = "openbmb/MiniCPM-V-4-gguf/mmproj-model-f16.gguf" + + mmproj_local = download_subdir / Path(mmproj_filename).name if not mmproj_local.exists(): - print(f"Downloading mmproj: {Path(mmproj_filename).name}") + print(f"Downloading vision model: {Path(mmproj_filename).name}...") repo_path, filename = mmproj_filename.rsplit("/", 1) - mmproj_local = Path(hf_hub_download( - repo_id=repo_path, - filename=filename, - local_dir=str(llm_models_dir), - local_dir_use_symlinks=False - )).resolve() + try: + mmproj_local = Path(hf_hub_download( + repo_id=repo_path, + filename=filename, + local_dir=str(download_subdir) + )).resolve() + except Exception as e: + print(f"Vision model download failed: {e}") + raise n_ctx = MODEL_SETTINGS["context_window"] n_batch = 2048 @@ -107,16 +136,37 @@ class MiniCPM_GGUF_Models: ) except Exception as model_error: error_msg = str(model_error).lower() - if "unknown minicpmv version" in error_msg: - raise RuntimeError( - f"MiniCPM version compatibility issue detected.\n" - f"Your llama-cpp-python version doesn't support this model.\n" - f"Try:\n" - f"1. Update llama-cpp-python: pip install --upgrade llama-cpp-python\n" - f"2. Try different llama-cpp-python version: pip install llama-cpp-python==0.2.90\n" - f"3. Or use the original MiniCPM transformers node instead\n" - f"Original error: {model_error}" - ) + if "unknown minicpmv version" in error_msg or "unsupported minicpmv version" in error_msg: + # Check if this is a V4.5 model + is_v45_model = any([ + "4.5" in model.lower(), + "4_5" in model.lower(), + "v4.5" in model.lower() + ]) + + if is_v45_model: + raise RuntimeError( + f"MiniCPM-V-4.5 compatibility issue detected.\n" + f"MiniCPM-V-4.5 support was just added to llama.cpp on Aug 26, 2025 (PR #15575).\n" + f"Your llama-cpp-python 0.3.16 was compiled before this update.\n\n" + f"Solutions:\n" + f"1. 🔄 Wait for new llama-cpp-python release (recommended - should be available soon)\n" + f"2. 🔨 Compile from source: pip uninstall llama-cpp-python && pip install llama-cpp-python --force-reinstall --no-cache-dir\n" + f"3. 🎯 Use MiniCPM-V-4.5 Transformers node instead (works perfectly)\n" + f"4. 🔙 Use MiniCPM-V-4.0 GGUF models (fully supported)\n\n" + f"Background: MiniCPM-V-4.5 GGUF support requires the latest llama.cpp code.\n" + f"Original error: {model_error}" + ) + else: + raise RuntimeError( + f"MiniCPM version compatibility issue detected.\n" + f"Your llama-cpp-python version doesn't support this model.\n\n" + f"Try:\n" + f"1. Update llama-cpp-python: pip install --upgrade llama-cpp-python\n" + f"2. Try different version: pip install llama-cpp-python==0.2.90\n" + f"3. Use the MiniCPM transformers node instead\n\n" + f"Original error: {model_error}" + ) else: raise model_error @@ -222,19 +272,6 @@ class MiniCPM_GGUF_Models: return f"Generation error: {str(e)}" finally: gc.collect() - - # def _clean_output(self, text: str) -> str: - # if not text: - # return text - # text = re.sub(r'^[\s\-•*]+', '', text) - # text = re.sub(r'^(?!1\.)\d+[\.\)\s\-]+', '', text) - # # text = re.sub(r'^\d+[\.\)\s\-]+', '', text) - # text = re.sub(r'^(Assistant|User|MiniCPM|AI):\s*', '', text, flags=re.IGNORECASE) - # text = re.sub(r'^[A-Z][a-z]+:\s*', '', text) - # text = text.strip() - # if not text: - # return "Unable to generate description." - # return text def _clean_output(self, text: str) -> str: if not text: @@ -352,7 +389,6 @@ class MiniCPM_GGUF_Base: frames = [ToPILImage()(v.permute([2, 0, 1])).convert("RGB") for v in frames] return frames - class AILab_MiniCPM_4_V_GGUF(MiniCPM_GGUF_Base): @classmethod def INPUT_TYPES(cls): @@ -374,7 +410,7 @@ class AILab_MiniCPM_4_V_GGUF(MiniCPM_GGUF_Base): RETURN_TYPES = ("STRING",) RETURN_NAMES = ("STRING",) FUNCTION = "generate" - CATEGORY = "🧪AILab/MiniCPM" + CATEGORY = "🧪AILab/📝MiniCPM" def generate(self, image=None, video=None, model=None, preset_prompt="Describe", custom_prompt="", device="Auto", memory_management="Keep in Memory", seed=-1): try: @@ -444,7 +480,7 @@ class AILab_MiniCPM_4_V_GGUF_Advanced(MiniCPM_GGUF_Base): RETURN_TYPES = ("STRING", "STRING") RETURN_NAMES = ("PROMPT", "STRING") FUNCTION = "generate" - CATEGORY = "🧪AILab/MiniCPM" + CATEGORY = "🧪AILab/📝MiniCPM" def generate(self, image=None, video=None, model=None, preset_prompt="Describe", custom_prompt="", system_prompt="", max_new_tokens=None, temperature=None, top_p=None, top_k=None, repetition_penalty=None, video_max_num_frames=64, video_max_slice_nums=2, device="Auto", memory_management="Keep in Memory", seed=-1): try: diff --git a/__init__.py b/__init__.py index de868e6..4dafb21 100644 --- a/__init__.py +++ b/__init__.py @@ -20,27 +20,16 @@ try: import llama_cpp GGUF_AVAILABLE = True except ImportError: - # Use Windows color codes for better visibility - print("\n" + "=" * 80) - print("\033[91mWARNING: llama-cpp-python library not found, GGUF functionality is not available\033[0m") - print("\033[93mTo use GGUF features, install additional dependencies:\033[0m") - print("\033[96mpip install llama-cpp-python\033[0m") - - # Check if installation guide exists and provide link - install_guide = current_dir / "llama_cpp_install.md" - if install_guide.exists(): - print("\033[93mFor detailed installation instructions with CUDA support, please see:\033[0m") - print(f"\033[96m{install_guide}\033[0m") - - print("\033[92mBasic MiniCPM functionality is still available\033[0m") - print("=" * 80 + "\n") + print("\033[93m⚠️ GGUF functionality unavailable - install llama-cpp-python for GGUF support\033[0m") + print("\033[96m📖 Installation guide: https://github.com/1038lab/ComfyUI-MiniCPM/tree/main/llama_cpp_install\033[0m") except Exception as e: - print("\n" + "=" * 80) - print(f"\033[91mError loading GGUF dependencies: {str(e)}\033[0m") - print("\033[92mBasic MiniCPM functionality is still available\033[0m") - print("=" * 80 + "\n") + print(f"\033[91m❌ GGUF loading error: {str(e)}\033[0m") + print("\033[96m📖 Installation guide: https://github.com/1038lab/ComfyUI-MiniCPM/tree/main/llama_cpp_install\033[0m") # Process all Python files in the directory (auto-registration functionality) +loaded_modules = [] +skipped_modules = [] + for file in current_dir.glob('*.py'): if file.name not in EXCLUDE_FILES: try: @@ -51,24 +40,26 @@ for file in current_dir.glob('*.py'): # Skip GGUF module if llama-cpp-python is not available if not GGUF_AVAILABLE and 'GGUF' in module_name: - print(f"\033[93mSkipping {module_name} - GGUF functionality not available\033[0m") + skipped_modules.append(module_name) continue spec.loader.exec_module(module) if hasattr(module, 'NODE_CLASS_MAPPINGS'): NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS) - print(f"\033[92mLoaded {module_name} nodes: {list(module.NODE_CLASS_MAPPINGS.keys())}\033[0m") + loaded_modules.append(module_name) if hasattr(module, 'NODE_DISPLAY_NAME_MAPPINGS'): NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS) except Exception as e: - print(f"\033[91mError loading module {module_name}: {str(e)}\033[0m") - if 'GGUF' not in module_name: # Only show warning for non-GGUF modules - print(f"\033[93mWarning: Failed to load {module_name} module\033[0m") + print(f"\033[91m❌ Failed to load {module_name}: {str(e)}\033[0m") + skipped_modules.append(module_name) -print(f"\n\033[92mMiniCPM nodes loaded: {list(NODE_CLASS_MAPPINGS.keys())}\033[0m") -print(f"\033[92mTotal nodes registered: {len(NODE_CLASS_MAPPINGS)}\033[0m") +# Summary output +if loaded_modules: + print(f"\033[92m✅ MiniCPM loaded: {len(NODE_CLASS_MAPPINGS)} nodes from {len(loaded_modules)} modules\033[0m") +if skipped_modules: + print(f"\033[93m⏭️ Skipped: {', '.join(skipped_modules)}\033[0m") __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']