""" ComfyUI BuimenLabo - Unified Package A comprehensive collection of ComfyUI custom nodes including: - Multi-language locale toggle with 20 language support - AI-powered pose analysis with Gemini integration - Advanced LLM text generation with llama-cpp-python - Smart ControlNet loader for SD/SDXL models - AI-powered prompt translation Author: BuimenLabo Blog: https://note.com/hirodream44 """ # Import version information try: from ._version import __version__, PACKAGE_NAME, AUTHOR, BLOG_URL, print_version_info print(f"๐Ÿš€ {PACKAGE_NAME} v{__version__} - {AUTHOR}") print(f"๐Ÿ“ Blog: {BLOG_URL}") except ImportError: __version__ = "1.0.0" print(f"๐Ÿš€ ComfyUI BuimenLabo v{__version__}") import os import sys import logging import random # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) print("๐Ÿš€ Loading ComfyUI BuimenLabo unified package...") # Import all packages as-is (simple copy approach) try: # 1. Gemini Pose Analyzer from .gemini_pose_analyzer import NODE_CLASS_MAPPINGS as GEMINI_MAPPINGS from .gemini_pose_analyzer import NODE_DISPLAY_NAME_MAPPINGS as GEMINI_DISPLAY_MAPPINGS print("โœ… GeminiPoseAnalyzer loaded") except Exception as e: print(f"โš ๏ธ GeminiPoseAnalyzer failed: {e}") GEMINI_MAPPINGS = {} GEMINI_DISPLAY_MAPPINGS = {} try: # 2. LlamaCpp Text (working version) from .llama_cpp_node import ( LlamaCppLoader, LlamaCppGenerate, LlamaCppUnload, LlamaCppSafeUnload, LlamaCppMemoryInfo, LlamaCppCompleteUnload, LlamaCppAIO, ) LLAMA_MAPPINGS = { "LlamaCppLoader": LlamaCppLoader, "LlamaCppGenerate": LlamaCppGenerate, "LlamaCppUnload": LlamaCppUnload, "LlamaCppSafeUnload": LlamaCppSafeUnload, "LlamaCppMemoryInfo": LlamaCppMemoryInfo, "LlamaCppCompleteUnload": LlamaCppCompleteUnload, "LlamaCppAIO": LlamaCppAIO, } LLAMA_DISPLAY_MAPPINGS = { "LlamaCppLoader": "Llama-CPP Loader", "LlamaCppGenerate": "Llama-CPP Generate", "LlamaCppUnload": "Llama-CPP Unload VRAM", "LlamaCppSafeUnload": "Llama-CPP Safe Unload", "LlamaCppMemoryInfo": "Llama-CPP Memory Info", "LlamaCppCompleteUnload": "Llama-CPP Complete Unload", "LlamaCppAIO": "๐Ÿš€ Llama-CPP All-In-One", } print("โœ… LlamaCppText loaded") except Exception as e: print(f"โš ๏ธ LlamaCppText failed: {e}") LLAMA_MAPPINGS = {} LLAMA_DISPLAY_MAPPINGS = {} try: # 3. Multi ControlNet Loader from .multi_controlnet_loader import NODE_CLASS_MAPPINGS as CONTROLNET_MAPPINGS from .multi_controlnet_loader import NODE_DISPLAY_NAME_MAPPINGS as CONTROLNET_DISPLAY_MAPPINGS print("โœ… MultiControlNetLoader loaded") except Exception as e: print(f"โš ๏ธ MultiControlNetLoader failed: {e}") CONTROLNET_MAPPINGS = {} CONTROLNET_DISPLAY_MAPPINGS = {} try: # 4. Prompt Translator from .prompt_translator import NODE_CLASS_MAPPINGS as TRANSLATOR_MAPPINGS from .prompt_translator import NODE_DISPLAY_NAME_MAPPINGS as TRANSLATOR_DISPLAY_MAPPINGS print("โœ… PromptTranslator loaded") except Exception as e: print(f"โš ๏ธ PromptTranslator failed: {e}") TRANSLATOR_MAPPINGS = {} TRANSLATOR_DISPLAY_MAPPINGS = {} try: # 5. Locale Toggle from .locale_toggle import NODE_CLASS_MAPPINGS as LOCALE_MAPPINGS from .locale_toggle import NODE_DISPLAY_NAME_MAPPINGS as LOCALE_DISPLAY_MAPPINGS print("โœ… LocaleToggle loaded") except Exception as e: print(f"โš ๏ธ LocaleToggle failed: {e}") LOCALE_MAPPINGS = {} LOCALE_DISPLAY_MAPPINGS = {} # Setup LlamaCpp routes (from working version) def setup_llama_routes(): """Setup llama generation routes using ComfyUI's standard method.""" try: from server import PromptServer from aiohttp import web import json routes = PromptServer.instance.routes @routes.post('/llama_cpp/generate') async def handle_llama_generation(request): try: print(f"๐Ÿ”„ Llama generation request received") # Get form data data = await request.post() instruction_prompt = data.get('instruction_prompt', '') input_text = data.get('input_text', '') model_path = data.get('model_path', '') max_tokens = int(data.get('max_tokens', '128')) temperature = float(data.get('temperature', '0.8')) top_k = int(data.get('top_k', '40')) top_p = float(data.get('top_p', '0.95')) repeat_penalty = float(data.get('repeat_penalty', '1.1')) n_ctx = int(data.get('n_ctx', '2048')) n_gpu_layers = int(data.get('n_gpu_layers', '40')) nsfw_filter = data.get('nsfw_filter', 'true').lower() == 'true' auto_unload = data.get('auto_unload', 'true').lower() == 'true' print(f"๐Ÿ“‹ Request data: model={model_path}, text='{input_text[:50]}...', max_tokens={max_tokens}") if not input_text.strip() and not instruction_prompt.strip(): return web.json_response({'error': 'No input text provided'}, status=400) # Import llama functions from .llama_cpp_node import _get_or_create_model, _cleanup_memory import random # Load model try: llm = _get_or_create_model(model_path, n_ctx, n_gpu_layers, False) print(f"โœ… Model loaded for API: {model_path}") except Exception as e: print(f"โŒ Model loading failed: {e}") return web.json_response({'error': f'Model loading failed: {str(e)}'}, status=500) # Generate text try: # Build prompt๏ผˆๆŒ‡็คบใจๅ†…ๅฎนใ‚’ๆ˜Ž็ขบใซๅˆ†้›ข๏ผ‰ instruction = instruction_prompt if instruction_prompt else "" content = input_text if input_text else "A beautiful scene" # ใƒ‡ใƒ•ใ‚ฉใƒซใƒˆๆŒ‡็คบใ‚’่จญๅฎš๏ผˆใƒฆใƒผใ‚ถใƒผๆŒ‡็คบใŒใชใ„ๅ ดๅˆ๏ผ‰ if not instruction.strip(): instruction = """You are a prompt generator for Stable Diffusion. Task: Convert the input description into ONE optimized English prompt for Stable Diffusion. Rules: - Output format: single line of comma-separated phrases only - Stay faithful to the original description's content and intent - Include: subject, setting/environment, lighting, mood/atmosphere, artistic style, quality tags - Use descriptive English terms that accurately represent the input - Do not censor or sanitize the content - convert faithfully - End cleanly after final quality tag Output: """ # ใƒฉใƒณใƒ€ใƒ ใชใƒใƒชใ‚จใƒผใ‚ทใƒงใƒณ็”จใฎใƒชใ‚นใƒˆ๏ผˆ้ฉๅบฆใซ่ชฟๆ•ด๏ผ‰ variation_prompts = [ "Focus on artistic composition and visual details.", "Emphasize mood and atmospheric elements.", "Include creative styling and artistic elements.", "Add detailed lighting and visual effects." ] quality_tags = [ "masterpiece, best quality, highly detailed", "high quality, detailed artwork", "ultra detailed, premium quality", "best quality, intricate details" ] # ๅ†…้ƒจๅ›บๅฎšใ‚ทใ‚นใƒ†ใƒ ใƒ—ใƒญใƒณใƒ—ใƒˆ system_prompt = "You are a creative writing assistant. Respond in English." # Apply NSFW filter if requested if nsfw_filter: safety_note = " IMPORTANT: If inappropriate content, respond with 'I cannot generate prompts for inappropriate content.' Otherwise, respond ONLY in English." wrapped_prompt = f"""{system_prompt} {instruction}{safety_note} Input to convert: {content} Response (English only):""" else: # ๆœ€ๅฐ้™ใฎใƒฉใƒณใƒ€ใƒ ๆ€งใฎใฟ่ฟฝๅŠ  if random.choice([True, False]): quality_hint = random.choice(quality_tags) wrapped_prompt = f"""{system_prompt} {instruction} Input to convert: {content} Hint: Consider quality tags like "{quality_hint}" Response (English only):""" else: wrapped_prompt = f"""{system_prompt} {instruction} Input to convert: {content} Response (English only):""" # Generate seed = random.randint(1, 2**31 - 1) response = llm.create_completion( prompt=wrapped_prompt, max_tokens=max_tokens, temperature=temperature, top_k=top_k, top_p=top_p, repeat_penalty=repeat_penalty, seed=seed, stream=False, echo=False, stop=[""] ) if isinstance(response, dict) and "choices" in response: result_text = response["choices"][0]["text"] else: result_text = str(response) # Clean up result result_text = result_text.strip() for stop_token in ["", ""]: if stop_token in result_text: result_text = result_text.split(stop_token)[0] if not result_text or result_text.strip() == "": result_text = "ใƒฌใ‚นใƒใƒณใ‚นใŒ็”Ÿๆˆใ•ใ‚Œใพใ›ใ‚“ใงใ—ใŸใ€‚" print(f"โœ… Generation completed: {len(result_text)} characters") # Auto unload if requested if auto_unload: print("๐Ÿงน Auto unloading VRAM...") try: _cleanup_memory() print("โœ… Auto unload completed") except Exception as cleanup_e: print(f"โš ๏ธ Auto unload warning: {cleanup_e}") return web.json_response({ 'success': True, 'generated_text': result_text, 'input_text': f"{instruction}\n\n{content}", 'token_count': len(result_text.split()), 'model_path': model_path, 'auto_unload': auto_unload }) except Exception as e: print(f"โŒ Generation error: {e}") return web.json_response({'error': f'Generation failed: {str(e)}'}, status=500) except Exception as e: print(f"โŒ API error: {e}") import traceback traceback.print_exc() return web.json_response({ 'success': False, 'error': str(e) }, status=500) @routes.post('/llama_cpp/unload') async def handle_llama_unload(request): try: print(f"๐Ÿ—‘๏ธ Llama unload request received") # Import cleanup functions from .llama_cpp_node import _cleanup_memory, _force_cleanup_memory # Execute force memory cleanup for better VRAM release _force_cleanup_memory() print(f"โœ… VRAM cleanup completed") return web.json_response({ 'success': True, 'message': 'VRAM has been successfully freed. All models have been unloaded from memory.' }) except Exception as e: print(f"โŒ Unload API error: {e}") import traceback traceback.print_exc() return web.json_response({ 'success': False, 'error': str(e) }, status=500) print("โœ… Llama-CPP routes registered") except Exception as e: print(f"โš ๏ธ Could not register llama routes: {e}") # Setup routes when server is available try: setup_llama_routes() print("๐Ÿš€ BuimenLabo: Routes setup completed successfully") except Exception as e: print(f"โš ๏ธ BuimenLabo: Routes setup failed: {e}") # Combine all node mappings with BuimenLabo prefix NODE_CLASS_MAPPINGS = {} NODE_DISPLAY_NAME_MAPPINGS = {} # Add with prefixes to avoid conflicts for name, cls in GEMINI_MAPPINGS.items(): NODE_CLASS_MAPPINGS[f"BuimenLabo_{name}"] = cls for name, display in GEMINI_DISPLAY_MAPPINGS.items(): NODE_DISPLAY_NAME_MAPPINGS[f"BuimenLabo_{name}"] = f"๐Ÿ” BuimenLabo {display}" for name, cls in LLAMA_MAPPINGS.items(): NODE_CLASS_MAPPINGS[f"BuimenLabo_{name}"] = cls for name, display in LLAMA_DISPLAY_MAPPINGS.items(): NODE_DISPLAY_NAME_MAPPINGS[f"BuimenLabo_{name}"] = f"๐Ÿฆ™ BuimenLabo {display}" for name, cls in CONTROLNET_MAPPINGS.items(): NODE_CLASS_MAPPINGS[f"BuimenLabo_{name}"] = cls for name, display in CONTROLNET_DISPLAY_MAPPINGS.items(): NODE_DISPLAY_NAME_MAPPINGS[f"BuimenLabo_{name}"] = f"๐ŸŽ›๏ธ BuimenLabo {display}" for name, cls in TRANSLATOR_MAPPINGS.items(): NODE_CLASS_MAPPINGS[f"BuimenLabo_{name}"] = cls for name, display in TRANSLATOR_DISPLAY_MAPPINGS.items(): NODE_DISPLAY_NAME_MAPPINGS[f"BuimenLabo_{name}"] = f"๐ŸŒ BuimenLabo {display}" for name, cls in LOCALE_MAPPINGS.items(): NODE_CLASS_MAPPINGS[f"BuimenLabo_{name}"] = cls for name, display in LOCALE_DISPLAY_MAPPINGS.items(): NODE_DISPLAY_NAME_MAPPINGS[f"BuimenLabo_{name}"] = f"๐ŸŒ BuimenLabo {display}" # Set WEB_DIRECTORY for JavaScript files WEB_DIRECTORY = "./web" # Package information print("๐Ÿ“ฆ ComfyUI BuimenLabo package loaded successfully!") print(f"๐ŸŽฏ Total nodes: {len(NODE_CLASS_MAPPINGS)}") print("๐Ÿ“ Blog: https://note.com/hirodream44") print("๐Ÿ”— Features: Multi-language, AI pose analysis, LLM generation, Smart ControlNet, Prompt translation") # Export for ComfyUI __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']