From df4ef85579fe780633ee079bc2954bede7fe20e8 Mon Sep 17 00:00:00 2001 From: diodiogod <160325160+diodiogod@users.noreply.github.com> Date: Sun, 21 Sep 2025 23:58:45 -0300 Subject: [PATCH] Version 4.9.25 Fix transformers compatibility handling Technical changes: - Disable legacy DynamicCache patches that masked version conflicts - Add startup version check for transformers >=4.51.3 - Provide clear upgrade instructions for users with old transformers - Remove automatic compatibility patches from VibeVoice engine --- CHANGELOG.md | 12 +++ README.md | 2 +- engines/vibevoice_engine/__init__.py | 6 +- .../transformers_compatibility.py | 76 ++++++++++++---- nodes.py | 22 ++++- pyproject.toml | 2 +- utils/compatibility/transformers_patches.py | 87 ++++++++++++++----- 7 files changed, 160 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81d71490..a60951c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.9.25] - 2025-09-21 + +### Added + +- Add clear warnings for users with outdated transformers versions +- Provide exact upgrade commands when version conflicts detected + +### Fixed + +- Fix transformers compatibility issues +- Remove problematic compatibility patches that masked upgrade requirements +- Better error messages for DynamicCache property setter issues ## [4.9.24] - 2025-09-21 ### Added diff --git a/README.md b/README.md index b3616fb4..9b675782 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Dynamic TOML Badge][version-shield]][version-url] [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/diogogo) -# TTS Audio Suite v4.9.24 +# TTS Audio Suite v4.9.25 [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/diogogo) diff --git a/engines/vibevoice_engine/__init__.py b/engines/vibevoice_engine/__init__.py index 1cd77be3..d91a456a 100644 --- a/engines/vibevoice_engine/__init__.py +++ b/engines/vibevoice_engine/__init__.py @@ -13,9 +13,9 @@ try: from vibevoice.modular.modeling_vibevoice_inference import VibeVoiceForConditionalGenerationInference from vibevoice.processor.vibevoice_processor import VibeVoiceProcessor - # Apply compatibility patches for Transformers 4.56+ - from .transformers_compatibility import apply_all_compatibility_patches - apply_all_compatibility_patches() + # Note: Transformers compatibility patches disabled - users should upgrade to >=4.51.3 + # from .transformers_compatibility import apply_all_compatibility_patches + # apply_all_compatibility_patches() # If imports work, load our engine components from .vibevoice_engine import VibeVoiceEngine diff --git a/engines/vibevoice_engine/transformers_compatibility.py b/engines/vibevoice_engine/transformers_compatibility.py index 1b409d2c..c523aae5 100644 --- a/engines/vibevoice_engine/transformers_compatibility.py +++ b/engines/vibevoice_engine/transformers_compatibility.py @@ -99,39 +99,77 @@ def patch_prepare_cache_for_generation(): def patch_dynamic_cache_key_value_cache(): """ - Patch DynamicCache to add key_cache/value_cache properties for VibeVoice compatibility. - - VibeVoice tries to access .key_cache and .value_cache on DynamicCache objects, - but newer transformers versions use a different internal structure. + Patch DynamicCache to add key_cache/value_cache properties with setters for VibeVoice compatibility. + + Issue: Some transformers versions have key_cache/value_cache as read-only properties, + but DynamicCache.__init__() tries to assign to them directly, causing "no setter" errors. + + Solution: Replace existing properties with setter-enabled properties that use private attributes. """ try: from transformers.cache_utils import DynamicCache - + # Check if already patched if hasattr(DynamicCache, '_vibevoice_cache_patched'): return True - - def key_cache_property(self): - """Compatibility property for .key_cache access""" + + # Initialize private attributes if they don't exist + original_init = DynamicCache.__init__ + + def patched_init(self, *args, **kwargs): + # Initialize private storage attributes before calling original init + if not hasattr(self, '_key_cache'): + self._key_cache = [] + if not hasattr(self, '_value_cache'): + self._value_cache = [] + + # Try original init, but catch setter errors + try: + original_init(self, *args, **kwargs) + except AttributeError as e: + if "property" in str(e) and "no setter" in str(e): + # This is the exact error we're trying to fix + # Initialize the object manually + pass + else: + raise e + + def key_cache_getter(self): + """Compatibility getter for .key_cache access""" + if hasattr(self, '_key_cache'): + return self._key_cache + # Fallback to new structure if available if len(self) == 0: return [] return [self[i][0] if self[i] is not None and len(self[i]) >= 2 else None for i in range(len(self))] - - def value_cache_property(self): - """Compatibility property for .value_cache access""" + + def key_cache_setter(self, value): + """Compatibility setter for .key_cache assignment""" + self._key_cache = value + + def value_cache_getter(self): + """Compatibility getter for .value_cache access""" + if hasattr(self, '_value_cache'): + return self._value_cache + # Fallback to new structure if available if len(self) == 0: return [] return [self[i][1] if self[i] is not None and len(self[i]) >= 2 else None for i in range(len(self))] - - # Add properties if they don't exist - if not hasattr(DynamicCache, 'key_cache'): - DynamicCache.key_cache = property(key_cache_property) - if not hasattr(DynamicCache, 'value_cache'): - DynamicCache.value_cache = property(value_cache_property) - + + def value_cache_setter(self, value): + """Compatibility setter for .value_cache assignment""" + self._value_cache = value + + # Replace __init__ with patched version + DynamicCache.__init__ = patched_init + + # Replace or add properties with setters (always override) + DynamicCache.key_cache = property(key_cache_getter, key_cache_setter) + DynamicCache.value_cache = property(value_cache_getter, value_cache_setter) + # Mark as patched DynamicCache._vibevoice_cache_patched = True - logger.debug("Applied DynamicCache key_cache/value_cache compatibility patch for VibeVoice") + logger.debug("Applied DynamicCache key_cache/value_cache setter compatibility patch for VibeVoice") return True except ImportError: diff --git a/nodes.py b/nodes.py index 9045866c..d8e5974a 100644 --- a/nodes.py +++ b/nodes.py @@ -9,7 +9,7 @@ except ImportError: # Don't do anything - __init__.py already handled compatibility testing # Version and constants -VERSION = "4.9.24" +VERSION = "4.9.25" IS_DEV = False # Set to False for release builds VERSION_DISPLAY = f"v{VERSION}" + (" (dev)" if IS_DEV else "") SEPARATOR = "=" * 70 @@ -33,6 +33,26 @@ current_dir = os.path.dirname(__file__) if current_dir not in sys.path: sys.path.insert(0, current_dir) +# Check transformers version compatibility +try: + import transformers + from packaging import version + + required_version = "4.51.3" + current_version = transformers.__version__ + + if version.parse(current_version) < version.parse(required_version): + print(f"🚨 COMPATIBILITY WARNING:") + print(f" Transformers version {current_version} is too old (requires >={required_version})") + print(f" This WILL cause errors like 'DynamicCache property has no setter'") + print(f" 📋 SOLUTION: Run this command to upgrade:") + print(f" pip install --upgrade transformers>={required_version}") + print(f" (Or use your environment's package manager)") + print() +except Exception as e: + print(f"⚠️ Could not check transformers version: {e}") + print(" If you encounter DynamicCache errors, upgrade transformers to >=4.51.3") + # Import nodes using direct file loading to avoid package path issues def load_node_module(module_name, file_name): """Load a node module from the nodes directory""" diff --git a/pyproject.toml b/pyproject.toml index 11b27de9..a4e97819 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "tts_audio_suite" description = "TTS Audio Suite - Universal multi-engine TTS extension for ComfyUI with unified architecture supporting IndexTTS-2, ChatterBox, Chatterbox Multilingual TTS (Official 23-Lang), F5-TTS, Higgs Audio 2, VibeVoice, and RVC engines. It has character voice management, SRT subtitle TTS support, and audio processing capabilities." -version = "4.9.24" +version = "4.9.25" license = {file = "LICENSE"} dependencies = ["s3tokenizer>=0.1.7", "resemble-perth", "librosa", "scipy", "omegaconf", "accelerate>=1.6.0", "transformers>=4.51.3", "# Additional dependencies for SRT support and audio processing", "conformer>=0.3.2", "torch", "torchaudio", "numpy>=1.26.4,<2.3.0", "einops", "phonemizer", "g2p-en", "unidecode", "# Audio processing and timing dependencies", "soundfile", "resampy", "webrtcvad", "# Optional but recommended for better performance", "numba"] diff --git a/utils/compatibility/transformers_patches.py b/utils/compatibility/transformers_patches.py index b0be5396..65dc2f94 100644 --- a/utils/compatibility/transformers_patches.py +++ b/utils/compatibility/transformers_patches.py @@ -37,8 +37,8 @@ class TransformersPatches: cls.patch_flash_attention_kwargs(verbose=verbose) cls.patch_base_streamer(verbose=verbose) - # Skip DynamicCache patch - causes property setter conflicts with transformers 4.46.3+ - # cls.patch_dynamic_cache_properties(verbose=verbose) + # Skip DynamicCache patch - users should upgrade transformers instead + # cls.patch_dynamic_cache_properties(verbose=verbose) cls.patch_vibevoice_generation_methods(verbose=verbose) if verbose: @@ -139,42 +139,85 @@ class TransformersPatches: @classmethod def patch_dynamic_cache_properties(cls, verbose: bool = True): """ - Patch DynamicCache key_cache/value_cache properties - - Issue: transformers 4.56+ changed DynamicCache internal structure - Affects: VibeVoice cache access patterns + Patch DynamicCache key_cache/value_cache properties with setters + + Issue: Some transformers versions have key_cache/value_cache as read-only properties, + but DynamicCache.__init__() tries to assign to them directly, causing "no setter" errors. + Affects: All engines using transformers models (ChatterBox, VibeVoice, Index-TTS) """ if "dynamic_cache_properties" in cls._patches_applied: return - + try: from transformers.cache_utils import DynamicCache - + # Add compatibility properties if not already patched if not hasattr(DynamicCache, '_tts_suite_patched'): - - def key_cache_property(self): - """Compatibility property for .key_cache access""" + + # Store original __init__ method + original_init = DynamicCache.__init__ + + def patched_init(self, *args, **kwargs): + """Patched __init__ that handles property setter errors""" + # Initialize private storage attributes before calling original init + if not hasattr(self, '_key_cache'): + self._key_cache = [] + if not hasattr(self, '_value_cache'): + self._value_cache = [] + + # Try original init, but catch setter errors + try: + original_init(self, *args, **kwargs) + except AttributeError as e: + if "property" in str(e) and "no setter" in str(e): + # This is the exact error we're trying to fix + # Initialize the object manually + if verbose: + print(f" 🔧 DynamicCache setter error caught and handled: {e}") + pass + else: + raise e + + def key_cache_getter(self): + """Compatibility getter for .key_cache access""" + if hasattr(self, '_key_cache'): + return self._key_cache + # Fallback to new structure if available if len(self) == 0: return [] return [self[i][0] if self[i] is not None and len(self[i]) >= 2 else None for i in range(len(self))] - - def value_cache_property(self): - """Compatibility property for .value_cache access""" + + def key_cache_setter(self, value): + """Compatibility setter for .key_cache assignment""" + self._key_cache = value + + def value_cache_getter(self): + """Compatibility getter for .value_cache access""" + if hasattr(self, '_value_cache'): + return self._value_cache + # Fallback to new structure if available if len(self) == 0: return [] return [self[i][1] if self[i] is not None and len(self[i]) >= 2 else None for i in range(len(self))] - - # Add properties to the class - DynamicCache.key_cache = property(key_cache_property) - DynamicCache.value_cache = property(value_cache_property) + + def value_cache_setter(self, value): + """Compatibility setter for .value_cache assignment""" + self._value_cache = value + + # Replace __init__ with patched version + DynamicCache.__init__ = patched_init + + # Replace or add properties with setters (always override) + DynamicCache.key_cache = property(key_cache_getter, key_cache_setter) + DynamicCache.value_cache = property(value_cache_getter, value_cache_setter) + DynamicCache._tts_suite_patched = True - + if verbose: - print(" 🔧 DynamicCache compatibility properties added") - + print(" 🔧 DynamicCache compatibility properties with setters added") + cls._patches_applied.add("dynamic_cache_properties") - + except Exception as e: warnings.warn(f"DynamicCache patching failed: {e}")