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
This commit is contained in:
diodiogod
2025-09-21 23:58:45 -03:00
parent 52e0083a99
commit df4ef85579
7 changed files with 160 additions and 47 deletions
+12
View File
@@ -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
+1 -1
View File
@@ -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)
+3 -3
View File
@@ -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
@@ -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:
+21 -1
View File
@@ -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"""
+1 -1
View File
@@ -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"]
+65 -22
View File
@@ -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}")