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:
@@ -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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[![Dynamic TOML Badge][version-shield]][version-url]
|
||||
[](https://ko-fi.com/diogogo)
|
||||
|
||||
# TTS Audio Suite v4.9.24
|
||||
# TTS Audio Suite v4.9.25
|
||||
|
||||
[](https://ko-fi.com/diogogo)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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"]
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user