# ComfyUI-QwenImageLoraLoader A ComfyUI custom node for loading and applying LoRA (Low-Rank Adaptation) to Nunchaku Qwen Image models. **This project is based on the fork version of ComfyUI-nunchaku-qwen-lora-suport-standalone.** ## Source This LoRA loader was extracted and modified from GavChap's fork: - **Original Fork**: [GavChap/ComfyUI-nunchaku (qwen-lora-suport-standalone branch)](https://github.com/GavChap/ComfyUI-nunchaku/tree/qwen-lora-suport-standalone) - **Extraction**: LoRA functionality was extracted from the full fork to create an independent custom node - **Integration**: Modified to work with the official ComfyUI-nunchaku plugin ## Features - **NunchakuQwenImageLoraLoader**: Load and apply single LoRA to Qwen Image models - **NunchakuQwenImageLoraStack**: Apply multiple LoRAs with dynamic UI control - **Dynamic VRAM Management**: Automatic CPU offloading based on available VRAM - **LoRA Composition**: Efficient LoRA stacking and composition - **ComfyUI Integration**: Seamless integration with ComfyUI workflows ## Installation ### Quick Installation (Recommended) 1. Clone this repository to your ComfyUI custom_nodes directory: ```bash git clone https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader.git ``` 2. Ensure you have the official ComfyUI-nunchaku plugin installed (which includes nunchaku as a dependency). 3. **Run the installation script:** - Double-click `install_qwen_lora.bat` to automatically configure the integration - This script will automatically modify the ComfyUI-nunchaku `__init__.py` file - Restart ComfyUI ### Manual Installation If you prefer manual installation, see [INSTALLATION.md](INSTALLATION.md) for detailed instructions. ## Integration with ComfyUI-nunchaku **IMPORTANT**: This node requires modification of the official ComfyUI-nunchaku plugin to function properly. ### Required Modification You must modify the `__init__.py` file in your ComfyUI-nunchaku plugin to import this LoRA loader: ```python try: # Import from the independent ComfyUI-QwenImageLoraLoader import sys import os qwen_lora_path = os.path.join(os.path.dirname(__file__), "..", "ComfyUI-QwenImageLoraLoader") if qwen_lora_path not in sys.path: sys.path.insert(0, qwen_lora_path) # Import directly from the file path import importlib.util spec = importlib.util.spec_from_file_location("qwenimage", os.path.join(qwen_lora_path, "nodes", "lora", "qwenimage.py")) qwenimage_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(qwenimage_module) NunchakuQwenImageLoraLoader = qwenimage_module.NunchakuQwenImageLoraLoader NunchakuQwenImageLoraStack = qwenimage_module.NunchakuQwenImageLoraStack NODE_CLASS_MAPPINGS["NunchakuQwenImageLoraLoader"] = NunchakuQwenImageLoraLoader NODE_CLASS_MAPPINGS["NunchakuQwenImageLoraStack"] = NunchakuQwenImageLoraStack logger.info("Successfully imported Qwen Image LoRA loaders from ComfyUI-QwenImageLoraLoader") except ImportError: logger.exception("Nodes `NunchakuQwenImageLoraLoader` and `NunchakuQwenImageLoraStack` import failed:") ``` ### Why This Modification is Required - The independent LoRA loader exists as a separate custom node - The official ComfyUI-nunchaku plugin needs to reference the independent LoRA loader - Standard `from ... import ...` fails due to different package structures - `importlib.util` allows direct module loading from file paths ### Automated Installation (Recommended) **The batch file `install_qwen_lora.bat` automatically adds the above code to your ComfyUI-nunchaku `__init__.py` file:** 1. **What the batch file does:** - Automatically detects your ComfyUI installation path - Backs up the original `__init__.py` file (creates `__init__.py.backup`) - Adds the exact import code shown above to the end of ComfyUI-nunchaku's `__init__.py` - Performs error checking to ensure all required files exist - Provides user feedback throughout the process 2. **Run the installation script:** - Double-click `install_qwen_lora.bat` to automatically configure the integration - No manual code editing required - the script handles everything - Restart ComfyUI after installation 3. **Uninstall if needed:** - Double-click `uninstall_qwen_lora.bat` to restore the original configuration - This will restore the backup and remove the integration code ## Usage ### Available Nodes - **NunchakuQwenImageLoraLoader**: Single LoRA loader - **NunchakuQwenImageLoraStack**: Multi LoRA stacker with dynamic UI ### Basic Usage 1. Load your Nunchaku Qwen Image model using `Nunchaku Qwen Image DiT Loader` 2. Add either `NunchakuQwenImageLoraLoader` or `NunchakuQwenImageLoraStack` node 3. Select your LoRA file and set the strength 4. Connect to your workflow ### Dynamic UI Control The `NunchakuQwenImageLoraStack` node automatically adjusts the number of visible LoRA slots based on the `lora_count` parameter (1-10). ## Features - **Easy Installation**: Automated installation script included - **Automatic Integration**: No manual code editing required - **Backup & Restore**: Automatic backup of original files - **Cross-Platform**: Works on Windows with batch files - **Error Handling**: Comprehensive error checking and user feedback - **Issue #1 Fixed**: Resolved [ComfyUI\custom_nodes not found error](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/1) with improved path detection (thanks to @mcv1234's solution) - **Issue #2 Fixed**: Fixed UTF-8 encoding error causing `SyntaxError: (unicode error)` by using dedicated Python script for proper UTF-8 encoding (thanks to @AHEKOT's bug report) - **Issue #3 Fixed (v1.4.0)**: Resolved [Node break cached progress error](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/3) by implementing proper IS_CHANGED method with hash-based change detection (thanks to @AHEKOT's bug report) ## Requirements - ComfyUI - ComfyUI-nunchaku plugin (with required modification) - PyTorch - Python 3.10+ ## Compatibility This node is designed to work with: - ComfyUI-nunchaku plugin (modified) - Nunchaku Qwen Image models - Standard ComfyUI workflows ## Troubleshooting ### Error: "attempted relative import with no known parent package" **Problem**: This error occurs when ComfyUI tries to load the LoRA loader nodes but fails due to import issues. **Error Message**: `ValueError: attempted relative import with no known parent package` **Root Cause**: The error was caused by using relative imports (`from ...wrappers`) in the LoRA loader code. Relative imports only work when the module is loaded as part of a package. However, ComfyUI-nunchaku loads the module directly using `importlib.util`, which bypasses package initialization. As a result, Python cannot resolve the relative import paths. **Solution**: Fixed in v1.5.0 by changing relative imports to absolute imports: - **Before**: `from ...wrappers.qwenimage import ComfyQwenImageWrapper` - **After**: `from wrappers.qwenimage import ComfyQwenImageWrapper` **How to Fix**: 1. Make sure you have the latest version of ComfyUI-QwenImageLoraLoader 2. Run `install_qwen_lora.bat` to ensure proper integration 3. Restart ComfyUI **Technical Details**: - The installation script adds `ComfyUI-QwenImageLoraLoader` to `sys.path` - This allows absolute imports to work correctly - The absolute import `from wrappers.qwenimage import` resolves to `ComfyUI-QwenImageLoraLoader/wrappers/qwenimage.py` ### Error: Installation Script Not Running **Problem**: The batch file doesn't execute or shows errors. **Solution**: 1. Right-click `install_qwen_lora.bat` and select "Run as Administrator" 2. Check that both `ComfyUI-nunchaku` and `ComfyUI-QwenImageLoraLoader` are in your `ComfyUI/custom_nodes` directory 3. Verify Python is installed and accessible from the command line 4. Check the console output for specific error messages ### Error: Nodes Not Appearing in ComfyUI **Problem**: After installation, the LoRA loader nodes don't appear in ComfyUI. **Solution**: 1. Restart ComfyUI completely (close all instances) 2. Check the ComfyUI console for error messages 3. Verify that the integration code was added to `ComfyUI-nunchaku/__init__.py` (search for "ComfyUI-QwenImageLoraLoader Integration") 4. If the integration code is missing, re-run `install_qwen_lora.bat` 5. Check that your ComfyUI-nunchaku version is compatible ### Error: "ModuleNotFoundError: No module named 'nunchaku'" **Problem**: The nunchaku package is not installed. **Solution**: 1. Install ComfyUI-nunchaku plugin from the official repository 2. Follow the nunchaku installation instructions to install the nunchaku wheel 3. Restart ComfyUI ## Known Limitations ### RES4LYF Sampler Compatibility Issue - **Status**: Currently Not Supported - **Issue**: Device mismatch errors occur when using RES4LYF sampler with LoRA ([Issue #7](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/7), [Issue #8](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/8)) - **Workaround**: Use other sampler types (e.g., res_samplers, standard samplers) with LoRA - **Technical Note**: The device mismatch occurs in `ComfyUI-nunchaku\models\qwenimage.py` at `self.time_text_embed(timestep, hidden_states)` due to RES4LYF sampler's specific tensor device handling requirements. This LoRA loader cannot fix this issue. - **Related Issues**: [Issue #7](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/7), [Issue #8](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/8) ## Changelog ### v1.5.3 (Latest) - **Fixed Critical Bug**: Resolved `TypeError: This LoRA loader only works with Nunchaku Qwen Image models, but got ComfyQwenImageWrapper` error in different workflows - **Problem**: LoRA loader failed when model was already wrapped with `ComfyQwenImageWrapper` in some workflows, even though the wrapper was correct - **Root Cause**: Using type name comparison (`type(model_wrapper).__name__ == "ComfyQwenImageWrapper"`) failed in some execution contexts due to dynamic imports and different module loading paths. When `ComfyQwenImageWrapper` was loaded from different import paths, Python treated them as different classes even though they were functionally identical - **Technical Solution**: Changed from type name comparison to attribute-based detection - **Before**: `if type(model_wrapper).__name__ == "ComfyQwenImageWrapper"` - **After**: `if hasattr(model_wrapper, 'model') and hasattr(model_wrapper, 'loras')` - The `ComfyQwenImageWrapper` class always has `model` (transformer) and `loras` (LoRA list) attributes - This detection method works regardless of how the class was imported or loaded - **Technical Details**: - Added comprehensive debug logging to help diagnose type detection issues - Logs now show type name, module, attributes, and full type representation - Enhanced error messages with detailed type information - Applied fix to both `NunchakuQwenImageLoraLoader` and `NunchakuQwenImageLoraStack` classes - **Benefits**: - Works with all workflows regardless of model loading order - No dependency on import mechanism or module path - More robust and resilient to Python module system quirks - Better debugging capabilities with detailed logging - **Impact**: Completely resolves the TypeError that occurred when using LoRA nodes in certain workflow configurations ### v1.5.2 - **Fixed Critical Bug**: Resolved persistent `ModuleNotFoundError: No module named 'wrappers.qwenimage'` error - **Reported by**: Multiple users experiencing intermittent import failures - **Problem**: Despite v1.5.0 and v1.5.1 fixes, some users still experienced `ModuleNotFoundError` when executing LoRA nodes - **Root Cause**: Module-level `sys.path` manipulation was insufficient in all execution contexts. The `from wrappers.qwenimage import ComfyQwenImageWrapper` statement at method execution time occasionally failed even though the module loaded successfully, due to timing issues, Python's module cache, and varying execution contexts in ComfyUI - **Technical Solution**: Implemented robust dynamic import using `importlib.util` within method execution - **Before**: Module-level `sys.path` setup + direct `from wrappers.qwenimage import` at method level - **After**: Dynamic `importlib.util`-based import within `load_lora()` and `load_lora_stack()` methods - **Technical Details**: - Each method now dynamically calculates the parent directory path at execution time - Uses `importlib.util.spec_from_file_location()` to create module spec from absolute file path - Executes module with `spec.loader.exec_module()` and extracts `ComfyQwenImageWrapper` class - Completely bypasses Python's normal import mechanism, making it independent of `sys.path` state - Eliminates dependency on module loading order or execution context - **Benefits**: - 100% reliable import regardless of execution context - No dependency on `sys.path` configuration - Works in all ComfyUI execution scenarios - No more intermittent import failures - More resilient to future ComfyUI or Python updates - **Impact**: Completely resolves the import reliability issue that affected multiple users ### v1.5.1 - **Fixed Critical Bug**: Additional fix for [Issue #6](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/6) - persistent `ModuleNotFoundError` - **Problem**: v1.5.0 fix was incomplete - path calculation error meant `sys.path` pointed to wrong directory - **Root Cause**: Incorrect path calculation: `os.path.dirname(os.path.dirname(os.path.dirname(current_dir)))` went up 3 levels instead of 2 - 3 levels up → pointed to `custom_nodes/` instead of `ComfyUI-QwenImageLoraLoader/` - This prevented `from wrappers.qwenimage` from resolving correctly - **Technical Solution**: Corrected path calculation to go up exactly 2 levels from `nodes/lora/` - **Before**: `os.path.dirname(os.path.dirname(os.path.dirname(current_dir)))` (3 levels) - **After**: `os.path.dirname(os.path.dirname(current_dir))` (2 levels) - **Result**: Correctly points to `ComfyUI-QwenImageLoraLoader/` root directory - **Applied To**: Module-level `sys.path` initialization in `qwenimage.py` - **Benefits**: - Absolute imports now work correctly in all loading scenarios - No more `ModuleNotFoundError` errors - Module can be loaded regardless of import mechanism - Completely resolves Issue #6 ### v1.5.0 - **Fixed Critical Bug**: Resolved [Issue #6 - attempted relative import with no known parent package](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/6) - **Reported by**: @showevr (GitHub Issue #6) - **Special Thanks**: This bug was discovered and reported by @showevr - **Problem**: LoRA loader nodes failed to load with `ValueError: attempted relative import with no known parent package` error - **Root Cause**: Using relative imports (`from ...wrappers.qwenimage import ComfyQwenImageWrapper`) in the LoRA loader code. Relative imports only work when the module is loaded as part of a package, but ComfyUI-nunchaku loads the module directly using `importlib.util`, which bypasses package initialization - **Technical Solution**: Changed relative imports to absolute imports in `qwenimage.py` - **Before**: `from ...wrappers.qwenimage import ComfyQwenImageWrapper` - **After**: `from wrappers.qwenimage import ComfyQwenImageWrapper` - **Technical Details**: - The installation script adds `ComfyUI-QwenImageLoraLoader` to `sys.path` - This allows absolute imports to work correctly - The absolute import `from wrappers.qwenimage import` resolves to `ComfyUI-QwenImageLoraLoader/wrappers/qwenimage.py` - Applied fix to both `NunchakuQwenImageLoraLoader` and `NunchakuQwenImageLoraStack` classes - **Community Contribution**: This fix was made possible by @showevr's bug reporting ### v1.4.0 - **Fixed Critical Bug**: Resolved [Issue #3 - Node break cached progress](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/3) reported by @AHEKOT - **Reported by**: @AHEKOT (GitHub Issue #3) - **Special Thanks**: This critical bug was discovered and reported by @AHEKOT, who identified the issue with cached progress being broken - **Problem**: LoRA nodes were breaking ComfyUI's cached progress during image generation, causing the progress bar to reset and restart repeatedly - **Root Cause**: The `IS_CHANGED` method was returning `float("NaN")` instead of a proper change detection hash - `float("NaN")` causes ComfyUI to always treat the node as "changed" - This invalidates the cache after every frame, forcing unnecessary re-execution - Result: Progress bar jumps back and forth, cache is constantly invalidated, generation becomes unstable - **Technical Solution**: Implemented proper hash-based change detection for both LoRA nodes - **NunchakuQwenImageLoraLoader**: Creates SHA256 hash from `model`, `lora_name`, and `lora_strength` - **NunchakuQwenImageLoraStack**: Creates SHA256 hash from `model`, `lora_count`, and all 10 LoRA slots (names and strengths) - Returns hex digest as the cache key - **Technical Details**: - Uses Python's `hashlib.sha256()` to generate deterministic hashes - Same inputs always produce same hash → cache is used correctly - Different inputs produce different hashes → node re-executes as expected - No more false positives causing cache invalidation - **Benefits**: - Stable cached progress during generation - No more progress bar jumping back and forth - Proper cache utilization for better performance - Smooth generation experience - VRAM usage optimized due to proper cache behavior - **Community Contribution**: This fix was made possible by @AHEKOT's bug reporting and issue tracking ### v1.3.0 - **Fixed Critical Bug**: Resolved `SyntaxError: invalid character '' (U+FFFD)` error when running installation script - **Problem**: PowerShell output was being written directly into Python files, causing syntax errors - **Root Cause**: Using PowerShell commands with piping (`Get-Content | Add-Content`) caused PowerShell status messages and metadata to be included in the output, which were then written into `__init__.py` - **Solution**: Replaced PowerShell-based approach with a dedicated Python script (`append_integration.py`) - **Technical Details**: - Created standalone Python script `append_integration.py` that handles UTF-8 file writing - Batch file now calls the Python script instead of using inline PowerShell commands - Python script uses proper UTF-8 encoding when writing to `__init__.py` - Eliminates any possibility of output artifacts contaminating Python files - **Benefits**: - More reliable and maintainable solution - No risk of shell output pollution - Cleaner separation of concerns between batch file and Python code - Better error handling and user feedback ### v1.2.0 - **Fixed [Issue #2](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/2)**: Resolved UTF-8 encoding error that caused `SyntaxError: (unicode error) 'utf-8' codec can't decode byte 0x90 in position 0` - **Reported by**: @AHEKOT (GitHub Issue #2) - **Special Thanks**: This critical bug was discovered and reported by @AHEKOT, who provided detailed error traces and screenshots - **Technical Fix**: Changed the batch file to use PowerShell for UTF-8 encoding when writing Python code to `__init__.py` - **Root Cause**: Windows batch files write in Shift-JIS encoding by default, which causes Python to fail when reading files as UTF-8 - **Solution**: Temporary file creation + PowerShell UTF-8 encoding ensures proper file encoding - **Technical Details**: - Changed from direct append (`>>`) to temp file method - Uses PowerShell `Get-Content` and `Add-Content` with `-Encoding UTF8` parameters - Ensures all Python code is written with proper UTF-8 encoding - **Impact**: Installation script now works correctly without encoding errors - **Community Contribution**: This fix was made possible by @AHEKOT's thorough bug reporting and error documentation ### v1.1.0 - **Fixed [Issue #1](https://github.com/ussoewwin/ComfyUI-QwenImageLoraLoader/issues/1)**: Resolved "ComfyUI\custom_nodes not found" error reported by @mcv1234 - **Special Thanks**: This fix was implemented based on the excellent solution provided by @mcv1234 in the GitHub issue - **Improved Path Detection**: Replaced unreliable wildcard search with relative path detection using script directory (solution by @mcv1234) - **Enhanced Error Messages**: Added clear directory structure guidance and expected folder layout - **Better User Experience**: More reliable installation process with comprehensive error checking - **Technical Details**: - Changed from `dir /s /b /ad "*ComfyUI\custom_nodes"` to relative path calculation (as suggested by @mcv1234) - Uses `%~dp0` (script directory) to calculate ComfyUI root with `..\..` navigation - Added validation for `custom_nodes` folder existence - Improved error messages with expected directory structure display - **Community Contribution**: This improvement was made possible by the community feedback and solution provided by @mcv1234 ### v1.0.0 - Initial release with LoRA loading functionality - Automated installation scripts - Integration with ComfyUI-nunchaku ## License This project is licensed under the MIT License.