Files
ussoewwin-ComfyUI-QwenImage…/md/PR28_FIX_EXPLANATION.md
T

321 lines
9.9 KiB
Markdown

# Complete Explanation of PR #28 and Additional Fixes
---
## ① Proposal Content and What the Problem Was
### PR #28 Proposal Content
**Title**: `Fix missing 1 required positional argument: 'cpu_offload'`
**Problem Description**:
After the kjai node update, the following error occurred:
```
TypeError: load_lora() missing 1 required positional argument: 'cpu_offload'
```
or
```
TypeError: load_lora_stack() missing 1 required positional argument: 'cpu_offload'
```
### Root Cause
**Code Before Fix**:
```python
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str):
# cpu_offload is a required argument (no default value)
```
**Problems**:
1. **Required Parameter**: `cpu_offload` was a required parameter without a default value, causing errors when not provided
2. **ComfyUI Parameter Passing**: ComfyUI automatically passes `required` parameters from `INPUT_TYPES()` to methods, but kjai node updates changed how parameters are passed in some call paths
3. **Compatibility Issues**: In certain node invocation scenarios (old workflows, dynamic node generation, or kjai node updates), the parameter could be missing
**Why It Became a Problem**:
- Even though `cpu_offload` is defined as `required` in `INPUT_TYPES()`, ComfyUI's node invocation mechanism doesn't always guarantee that all parameters are passed
- The kjai node update modified how nodes are invoked, potentially causing parameter omissions in some call paths
- This is a compatibility issue that arose from external node updates
---
## ② How We Fixed It (File Names and Code)
### Files Modified
1. `nodes/lora/qwenimage.py` - 4 locations fixed
2. `nodes/lora/qwenimage_v2.py` - 2 locations fixed
### Fix 1: PR #28 Fixes (FUNCTION Methods)
#### File: `nodes/lora/qwenimage.py`
**Fix Location 1: `NunchakuQwenImageLoraLoader.load_lora()` method** (line 91)
```python
# Before
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str):
# After
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str = "disable"):
```
**Fix Location 2: `NunchakuQwenImageLoraStack.load_lora_stack()` method** (line 271)
```python
# Before
def load_lora_stack(self, model, lora_count, cpu_offload, **kwargs):
# After
def load_lora_stack(self, model, lora_count, cpu_offload="disable", **kwargs):
```
#### File: `nodes/lora/qwenimage_v2.py`
**Fix Location 3: `NunchakuQwenImageLoraStackV2.load_lora_stack()` method** (line 113)
```python
# Before
def load_lora_stack(self, model, lora_count, cpu_offload, **kwargs):
# After
def load_lora_stack(self, model, lora_count, cpu_offload="disable", **kwargs):
```
### Fix 2: Additional Fixes Applied (IS_CHANGED Methods)
#### File: `nodes/lora/qwenimage.py`
**Fix Location 4: `NunchakuQwenImageLoraLoader.IS_CHANGED()` method** (line 36)
```python
# Before
@classmethod
def IS_CHANGED(s, model, lora_name, lora_strength, cpu_offload, *args, **kwargs):
"""
Detect changes to trigger node re-execution.
Returns a hash of relevant parameters to detect changes.
"""
import hashlib
m = hashlib.sha256()
m.update(lora_name.encode())
m.update(str(lora_strength).encode())
m.update(str(model).encode())
m.update(cpu_offload.encode())
return m.digest().hex()
# After
@classmethod
def IS_CHANGED(s, model, lora_name, lora_strength, cpu_offload="disable", *args, **kwargs):
"""
Detect changes to trigger node re-execution.
Returns a hash of relevant parameters to detect changes.
"""
import hashlib
m = hashlib.sha256()
m.update(lora_name.encode())
m.update(str(lora_strength).encode())
m.update(str(model).encode())
m.update(cpu_offload.encode())
return m.digest().hex()
```
**Fix Location 5: `NunchakuQwenImageLoraStack.IS_CHANGED()` method** (line 195)
```python
# Before
@classmethod
def IS_CHANGED(cls, model, lora_count, cpu_offload, **kwargs):
"""
Detect changes to trigger node re-execution.
Returns a hash of relevant parameters to detect changes.
"""
import hashlib
m = hashlib.sha256()
m.update(str(model).encode())
m.update(str(lora_count).encode())
m.update(cpu_offload.encode())
# Hash all LoRA parameters
for i in range(1, 11):
m.update(kwargs.get(f"lora_name_{i}", "").encode())
m.update(str(kwargs.get(f"lora_strength_{i}", 0)).encode())
return m.digest().hex()
# After
@classmethod
def IS_CHANGED(cls, model, lora_count, cpu_offload="disable", **kwargs):
"""
Detect changes to trigger node re-execution.
Returns a hash of relevant parameters to detect changes.
"""
import hashlib
m = hashlib.sha256()
m.update(str(model).encode())
m.update(str(lora_count).encode())
m.update(cpu_offload.encode())
# Hash all LoRA parameters
for i in range(1, 11):
m.update(kwargs.get(f"lora_name_{i}", "").encode())
m.update(str(kwargs.get(f"lora_strength_{i}", 0)).encode())
return m.digest().hex()
```
#### File: `nodes/lora/qwenimage_v2.py`
**Fix Location 6: `NunchakuQwenImageLoraStackV2.IS_CHANGED()` method** (line 36)
```python
# Before
@classmethod
def IS_CHANGED(cls, model, lora_count, cpu_offload, **kwargs):
"""
Detect changes to trigger node re-execution.
Returns a hash of relevant parameters to detect changes.
"""
import hashlib
m = hashlib.sha256()
m.update(str(model).encode())
m.update(str(lora_count).encode())
m.update(cpu_offload.encode())
# Hash all LoRA parameters
for i in range(1, 11):
m.update(kwargs.get(f"lora_name_{i}", "").encode())
m.update(str(kwargs.get(f"lora_strength_{i}", 0)).encode())
return m.digest().hex()
# After
@classmethod
def IS_CHANGED(cls, model, lora_count, cpu_offload="disable", **kwargs):
"""
Detect changes to trigger node re-execution.
Returns a hash of relevant parameters to detect changes.
"""
import hashlib
m = hashlib.sha256()
m.update(str(model).encode())
m.update(str(lora_count).encode())
m.update(cpu_offload.encode())
# Hash all LoRA parameters
for i in range(1, 11):
m.update(kwargs.get(f"lora_name_{i}", "").encode())
m.update(str(kwargs.get(f"lora_strength_{i}", 0)).encode())
return m.digest().hex()
```
### Summary of Fixes
| File | Class | Method | Line | Fix Type |
|------|-------|--------|------|----------|
| `qwenimage.py` | `NunchakuQwenImageLoraLoader` | `load_lora()` | 91 | PR #28 |
| `qwenimage.py` | `NunchakuQwenImageLoraLoader` | `IS_CHANGED()` | 36 | Additional |
| `qwenimage.py` | `NunchakuQwenImageLoraStack` | `load_lora_stack()` | 271 | PR #28 |
| `qwenimage.py` | `NunchakuQwenImageLoraStack` | `IS_CHANGED()` | 195 | Additional |
| `qwenimage_v2.py` | `NunchakuQwenImageLoraStackV2` | `load_lora_stack()` | 113 | PR #28 |
| `qwenimage_v2.py` | `NunchakuQwenImageLoraStackV2` | `IS_CHANGED()` | 36 | Additional |
**Total**: 2 files, 6 locations fixed
---
## ③ What It Means
### 1. Technical Meaning
**Effect of Default Arguments**:
```python
# Before (required argument)
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str):
# Error if cpu_offload is not provided
# After (with default value)
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str = "disable"):
# Uses "disable" if cpu_offload is not provided
```
**Behavior**:
- If `cpu_offload` is passed, that value is used
- If `cpu_offload` is missing, the default value `"disable"` is automatically used
- This prevents `TypeError` and ensures the function always receives a valid value
### 2. Why `"disable"`?
Looking at the `INPUT_TYPES()` definition:
```python
"cpu_offload": (
["auto", "enable", "disable"],
{
"default": "disable",
"tooltip": "CPU offload setting. 'auto' enables offload when VRAM is low, 'enable' forces offload, 'disable' disables offload.",
},
),
```
**Reason**:
- The UI default value is `"disable"`
- To maintain consistency between UI defaults and implementation defaults
- `"disable"` means "no CPU offload" (better performance when VRAM is sufficient), which is the recommended default
### 3. Functional Meaning
**Fix for FUNCTION Methods**:
- Prevents errors when `cpu_offload` parameter is missing in `load_lora()` and `load_lora_stack()`
- Ensures compatibility with kjai node updates
**Fix for IS_CHANGED Methods**:
- ComfyUI calls `IS_CHANGED()` to detect changes and determine if node re-execution is needed
- If `cpu_offload` is missing in `IS_CHANGED()` calls, it could cause `AttributeError` when calling `.encode()`
- Adding default values prevents errors in change detection as well
### 4. Compatibility Meaning
**Backward Compatibility**:
- Existing workflows continue to work without modification
- Maintains compatibility with updated or new nodes
**Future-Proofing**:
- Prevents similar errors when parameters are missing in various invocation scenarios
- More robust integration with external nodes
### 5. Design Meaning
**Defensive Programming**:
- Protects against unexpected external factors (like node updates)
- Provides fallback behavior through default values
**Consistency**:
- UI defaults match implementation defaults
- User expectations align with actual behavior
### 6. Practical Meaning
**Error Prevention**:
- Eliminates `TypeError: missing 1 required positional argument` errors
- More robust operation
**User Experience**:
- Fewer errors for end users
- More stable operation
---
## Summary
### Problem
After the kjai node update, `cpu_offload` parameter could be missing, causing `TypeError`.
### Solution
Added default value `"disable"` to both FUNCTION methods (`load_lora`, `load_lora_stack`) and IS_CHANGED methods for all three node classes.
### Result
- ✅ Error prevention
- ✅ Backward compatibility maintained
- ✅ Consistency with UI defaults
- ✅ More robust implementation
This fix is a defensive programming approach to handle compatibility issues caused by external node updates.