Fixed 'dict' object has no attribute 'cpu' error when using Voice Designer
voices with TTS Text node.
## Root Cause
Voice Designer and Character Voices nodes output unified NARRATOR_VOICE format:
{
"audio": {"waveform": tensor, "sample_rate": 24000},
"audio_path": "...",
"reference_text": "...",
...
}
But TTS Text node was wrapping this AGAIN when creating voice_mapping:
{
"waveform": audio_tensor, # audio_tensor was already a dict!
"sample_rate": 24000,
"text": "..."
}
This created triple-nested structure that broke audio hashing.
## Fixes Applied
**TTS Text Node** (nodes/unified/tts_text_node.py):
- Extract actual waveform tensor from audio dict before creating voice_mapping
- Applied to all 6 places where audio_tensor is used:
- Narrator voice mapping (line 1151)
- Character ICL fallback (line 1183)
- Character x_vector fallback (line 1217)
- Character no-text fallback (line 1220)
- Character no-audio fallback (line 1237)
- Pattern: Check if audio_tensor is dict, extract waveform/sample_rate if so
**Qwen3-TTS Adapter** (engines/adapters/qwen3_tts_adapter.py):
- Extract inner audio dict before passing to hash function
- Changed from passing full voice_ref to passing voice_ref['audio']
**Audio Hash Utility** (utils/audio/audio_hash.py):
- Added nested structure handling with fallback
- Detects Character Voices format {"audio": {...}} vs direct format
- Enhanced debug logging to diagnose structure issues
## Testing
- Voice Designer → TTS Text: Works without hash error
- Character Voices → TTS Text: Works without hash error
- Audio hashing generates stable keys for caching
- Both VoiceDesign and traditional voices function correctly
614 lines
30 KiB
Python
614 lines
30 KiB
Python
"""
|
|
Transformers Compatibility Patches - DEPRECATED
|
|
|
|
⚠️ THIS MODULE IS NO LONGER USED ⚠️
|
|
|
|
Originally created to fix compatibility issues with transformers 4.46.3, but after
|
|
upgrading to transformers 4.51.3+ (as required by VibeVoice), these patches are no
|
|
longer needed and have been disabled.
|
|
|
|
Historical context - patches that were needed for transformers 4.46.3:
|
|
- FlashAttentionKwargs import location (moved in 4.46.3+)
|
|
- BaseStreamer import location (moved in 4.46.3+)
|
|
- DynamicCache key_cache/value_cache properties (changed in 4.56+)
|
|
- GenerationMixin._prepare_generation_config signature (changed in 4.46.3+)
|
|
- GenerationMixin._prepare_cache_for_generation signature (changed in 4.56+)
|
|
|
|
Solution: Updated to transformers>=4.51.3 as required by VibeVoice, eliminating
|
|
the need for these compatibility workarounds.
|
|
|
|
This file is kept for historical reference and potential future use.
|
|
"""
|
|
|
|
import warnings
|
|
from typing import Optional
|
|
|
|
|
|
class TransformersPatches:
|
|
"""Centralized transformers compatibility patches manager"""
|
|
|
|
_patches_applied = set()
|
|
|
|
@classmethod
|
|
def apply_all_patches(cls, verbose: bool = True):
|
|
"""Apply all necessary transformers compatibility patches"""
|
|
cls.patch_flash_attention_kwargs(verbose=verbose)
|
|
cls.patch_base_streamer(verbose=verbose)
|
|
# Skip DynamicCache patch - users should upgrade transformers instead
|
|
# cls.patch_dynamic_cache_properties(verbose=verbose)
|
|
cls.patch_vibevoice_generation_methods(verbose=verbose)
|
|
cls.patch_accelerate_compatibility(verbose=verbose)
|
|
# Step Audio EditX tokenization patch no longer needed - fixed via config.json
|
|
# cls.patch_step_audio_tokenization(verbose=verbose)
|
|
# Qwen3-TTS check_model_inputs patch no longer needed - fixed directly in bundled code
|
|
# cls.patch_qwen3_tts_check_model_inputs(verbose=verbose)
|
|
|
|
@classmethod
|
|
def patch_flash_attention_kwargs(cls, verbose: bool = True):
|
|
"""
|
|
Patch FlashAttentionKwargs import location compatibility
|
|
|
|
Issue: transformers 4.46.3+ moved FlashAttentionKwargs to different module
|
|
Affects: VibeVoice package imports
|
|
"""
|
|
if "flash_attention_kwargs" in cls._patches_applied:
|
|
return
|
|
|
|
try:
|
|
import transformers.modeling_flash_attention_utils
|
|
|
|
# Check if FlashAttentionKwargs is missing from old location
|
|
if not hasattr(transformers.modeling_flash_attention_utils, 'FlashAttentionKwargs'):
|
|
# Try to import from new locations and add to old location
|
|
try:
|
|
from transformers.utils import FlashAttentionKwargs
|
|
transformers.modeling_flash_attention_utils.FlashAttentionKwargs = FlashAttentionKwargs
|
|
if verbose:
|
|
print(" 🔧 FlashAttentionKwargs patched (from transformers.utils)")
|
|
except ImportError:
|
|
try:
|
|
from transformers.generation.utils import FlashAttentionKwargs
|
|
transformers.modeling_flash_attention_utils.FlashAttentionKwargs = FlashAttentionKwargs
|
|
if verbose:
|
|
print(" 🔧 FlashAttentionKwargs patched (from transformers.generation.utils)")
|
|
except ImportError:
|
|
# Create dummy implementation as fallback
|
|
class FlashAttentionKwargs:
|
|
def __init__(self, **kwargs):
|
|
for key, value in kwargs.items():
|
|
setattr(self, key, value)
|
|
|
|
transformers.modeling_flash_attention_utils.FlashAttentionKwargs = FlashAttentionKwargs
|
|
if verbose:
|
|
print(" 🔧 FlashAttentionKwargs patched (dummy implementation)")
|
|
|
|
cls._patches_applied.add("flash_attention_kwargs")
|
|
|
|
except Exception as e:
|
|
warnings.warn(f"FlashAttentionKwargs patching failed: {e}")
|
|
|
|
@classmethod
|
|
def patch_base_streamer(cls, verbose: bool = True):
|
|
"""
|
|
Patch BaseStreamer import location compatibility
|
|
|
|
Issue: transformers 4.46.3+ moved BaseStreamer to different module
|
|
Affects: VibeVoice package imports
|
|
"""
|
|
if "base_streamer" in cls._patches_applied:
|
|
return
|
|
|
|
try:
|
|
import transformers.generation
|
|
|
|
# Check if BaseStreamer is missing from old location
|
|
if not hasattr(transformers.generation, 'BaseStreamer'):
|
|
try:
|
|
from transformers.generation.streamers import BaseStreamer
|
|
transformers.generation.BaseStreamer = BaseStreamer
|
|
if verbose:
|
|
print(" 🔧 BaseStreamer patched (from transformers.generation.streamers)")
|
|
except ImportError:
|
|
try:
|
|
from transformers.generation.utils import BaseStreamer
|
|
transformers.generation.BaseStreamer = BaseStreamer
|
|
if verbose:
|
|
print(" 🔧 BaseStreamer patched (from transformers.generation.utils)")
|
|
except ImportError:
|
|
# Create dummy implementation as fallback
|
|
class BaseStreamer:
|
|
def __init__(self):
|
|
pass
|
|
|
|
def put(self, value):
|
|
pass
|
|
|
|
def end(self):
|
|
pass
|
|
|
|
transformers.generation.BaseStreamer = BaseStreamer
|
|
if verbose:
|
|
print(" 🔧 BaseStreamer patched (dummy implementation)")
|
|
|
|
cls._patches_applied.add("base_streamer")
|
|
|
|
except Exception as e:
|
|
warnings.warn(f"BaseStreamer patching failed: {e}")
|
|
|
|
@classmethod
|
|
def patch_dynamic_cache_properties(cls, verbose: bool = True):
|
|
"""
|
|
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'):
|
|
|
|
# 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 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))]
|
|
|
|
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 with setters added")
|
|
|
|
cls._patches_applied.add("dynamic_cache_properties")
|
|
|
|
except Exception as e:
|
|
warnings.warn(f"DynamicCache patching failed: {e}")
|
|
|
|
@classmethod
|
|
def patch_vibevoice_generation_methods(cls, verbose: bool = True):
|
|
"""
|
|
Patch VibeVoice generation method signatures
|
|
|
|
Issue: transformers 4.46.3+ changed method signatures for generation methods
|
|
Affects: VibeVoice model.generate() calls
|
|
"""
|
|
if "vibevoice_generation_methods" in cls._patches_applied:
|
|
return
|
|
|
|
try:
|
|
from vibevoice.modular.modeling_vibevoice_inference import VibeVoiceForConditionalGenerationInference
|
|
import inspect
|
|
|
|
# Patch the generate method to handle signature incompatibilities
|
|
original_generate = VibeVoiceForConditionalGenerationInference.generate
|
|
|
|
def patched_generate(model_self, *args, **kwargs):
|
|
"""Patched generate with signature compatibility fixes"""
|
|
|
|
# Apply method signature fixes only once per model instance
|
|
if not hasattr(model_self, '_generation_methods_patched'):
|
|
|
|
# Patch _prepare_cache_for_generation signature
|
|
original_prepare_cache = model_self._prepare_cache_for_generation
|
|
|
|
def safe_prepare_cache_for_generation(generation_config, model_kwargs, *remaining_args):
|
|
try:
|
|
sig = inspect.signature(original_prepare_cache)
|
|
if len(sig.parameters) == 5:
|
|
# New transformers version (4.56+)
|
|
return original_prepare_cache(generation_config, model_kwargs, remaining_args[0], remaining_args[1], remaining_args[2])
|
|
else:
|
|
# Old transformers version (pre-4.56)
|
|
return original_prepare_cache(generation_config, model_kwargs, None, remaining_args[0], remaining_args[1], remaining_args[2])
|
|
except Exception:
|
|
# Fallback to try both versions
|
|
try:
|
|
return original_prepare_cache(generation_config, model_kwargs, remaining_args[0], remaining_args[1], remaining_args[2])
|
|
except TypeError:
|
|
return original_prepare_cache(generation_config, model_kwargs, None, remaining_args[0], remaining_args[1], remaining_args[2])
|
|
|
|
model_self._prepare_cache_for_generation = safe_prepare_cache_for_generation
|
|
|
|
# Patch _prepare_generation_config signature
|
|
original_prepare_gen_config = model_self._prepare_generation_config
|
|
|
|
def safe_prepare_generation_config(*args, **kwargs):
|
|
try:
|
|
# Try calling with all arguments first
|
|
return original_prepare_gen_config(*args, **kwargs)
|
|
except TypeError as e:
|
|
if "takes 2 positional arguments but 3 were given" in str(e):
|
|
# transformers 4.46.3+ GenerationMixin._prepare_generation_config
|
|
# only takes (self, generation_config), not model_kwargs
|
|
# But we need to ensure bos_token_id is set if missing
|
|
generation_config = args[0] if len(args) >= 1 else None
|
|
|
|
if generation_config and not hasattr(generation_config, 'bos_token_id'):
|
|
# Set bos_token_id from tokenizer if missing
|
|
if hasattr(model_self, 'config') and hasattr(model_self.config, 'bos_token_id'):
|
|
generation_config.bos_token_id = model_self.config.bos_token_id
|
|
elif hasattr(model_self, 'generation_config') and hasattr(model_self.generation_config, 'bos_token_id'):
|
|
generation_config.bos_token_id = model_self.generation_config.bos_token_id
|
|
else:
|
|
# Use a reasonable default for Qwen tokenizer
|
|
generation_config.bos_token_id = 151643 # Qwen2 BOS token
|
|
|
|
return original_prepare_gen_config(generation_config)
|
|
else:
|
|
raise e
|
|
|
|
model_self._prepare_generation_config = safe_prepare_generation_config
|
|
model_self._generation_methods_patched = True
|
|
|
|
return original_generate(model_self, *args, **kwargs)
|
|
|
|
# Replace the generate method on the class
|
|
VibeVoiceForConditionalGenerationInference.generate = patched_generate
|
|
|
|
cls._patches_applied.add("vibevoice_generation_methods")
|
|
|
|
except ImportError:
|
|
# VibeVoice not installed, skip this patch
|
|
pass
|
|
except Exception as e:
|
|
warnings.warn(f"VibeVoice generation methods patching failed: {e}")
|
|
|
|
@classmethod
|
|
def patch_accelerate_compatibility(cls, verbose: bool = True):
|
|
"""
|
|
Fix accelerate detection and missing function compatibility issues.
|
|
|
|
Issue: transformers 4.56.1 + accelerate 0.20.3 compatibility problems:
|
|
1. transformers.utils.is_accelerate_available() returns False despite accelerate being installed
|
|
2. Missing check_tied_parameters_on_same_device function in older accelerate versions
|
|
Affects: VibeVoice model loading with device_map
|
|
"""
|
|
if "accelerate_compatibility" in cls._patches_applied:
|
|
return
|
|
|
|
try:
|
|
# Force import accelerate first
|
|
import accelerate
|
|
|
|
# Force import key accelerate components that transformers needs
|
|
from accelerate import infer_auto_device_map, dispatch_model
|
|
from accelerate.utils import get_balanced_memory
|
|
|
|
# Test if transformers can detect accelerate
|
|
from transformers.utils import is_accelerate_available
|
|
accelerate_detected = is_accelerate_available()
|
|
|
|
if not accelerate_detected:
|
|
if verbose:
|
|
print(" 🔧 Fixing broken transformers accelerate detection")
|
|
|
|
# Comprehensive monkey patch for all accelerate detection functions
|
|
import transformers.utils
|
|
def fixed_is_accelerate_available():
|
|
try:
|
|
import accelerate
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
# Replace the broken detection function in all locations
|
|
transformers.utils.is_accelerate_available = fixed_is_accelerate_available
|
|
|
|
try:
|
|
import transformers.modeling_utils
|
|
transformers.modeling_utils.is_accelerate_available = fixed_is_accelerate_available
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
import transformers.utils.import_utils
|
|
transformers.utils.import_utils.is_accelerate_available = fixed_is_accelerate_available
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
import transformers
|
|
if hasattr(transformers, 'is_accelerate_available'):
|
|
transformers.is_accelerate_available = fixed_is_accelerate_available
|
|
except:
|
|
pass
|
|
|
|
# Add missing functions that newer transformers expects
|
|
def check_tied_parameters_on_same_device(model, device_map=None):
|
|
"""
|
|
Dummy implementation of missing accelerate function.
|
|
This function checks if tied parameters are on the same device.
|
|
|
|
Args:
|
|
model: The model to check
|
|
device_map: Optional device mapping
|
|
|
|
Returns:
|
|
bool: True if tied parameters are on same device
|
|
"""
|
|
return True # For single GPU setups, this is always true
|
|
|
|
def dispatch_model(model, device_map=None, main_device=None, state_dict=None,
|
|
offload_dir=None, offload_index=None, **kwargs):
|
|
"""
|
|
Dummy implementation of missing accelerate.dispatch_model function.
|
|
For single GPU setups, we just move the model to the target device.
|
|
|
|
Args:
|
|
model: The model to dispatch
|
|
device_map: Device mapping (ignored in dummy implementation)
|
|
main_device: Main device to use
|
|
**kwargs: Additional arguments (ignored)
|
|
|
|
Returns:
|
|
The model (moved to device if specified)
|
|
"""
|
|
if main_device is not None:
|
|
model = model.to(main_device)
|
|
elif device_map == "cuda" or (isinstance(device_map, dict) and "cuda" in str(device_map)):
|
|
model = model.cuda()
|
|
return model
|
|
|
|
# Only add missing functions if we have compatibility issues
|
|
needs_function_patches = False
|
|
|
|
# Check if critical functions are missing (indicating compatibility issues)
|
|
try:
|
|
import accelerate.utils
|
|
if not hasattr(accelerate.utils, 'check_tied_parameters_on_same_device'):
|
|
needs_function_patches = True
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
import accelerate
|
|
if not hasattr(accelerate, 'dispatch_model'):
|
|
needs_function_patches = True
|
|
except:
|
|
pass
|
|
|
|
# Only apply function patches if we detected issues OR accelerate detection failed
|
|
if not accelerate_detected or needs_function_patches:
|
|
|
|
if verbose and needs_function_patches:
|
|
print(" 🔧 Adding missing accelerate functions")
|
|
|
|
# Add missing functions to accelerate.utils
|
|
try:
|
|
import accelerate.utils
|
|
if not hasattr(accelerate.utils, 'check_tied_parameters_on_same_device'):
|
|
accelerate.utils.check_tied_parameters_on_same_device = check_tied_parameters_on_same_device
|
|
except Exception as e:
|
|
if verbose and not accelerate_detected:
|
|
print(f" ⚠️ Could not add to accelerate.utils: {e}")
|
|
|
|
# Add dispatch_model to accelerate module
|
|
try:
|
|
import accelerate
|
|
if not hasattr(accelerate, 'dispatch_model'):
|
|
accelerate.dispatch_model = dispatch_model
|
|
except Exception as e:
|
|
if verbose and not accelerate_detected:
|
|
print(f" ⚠️ Could not add dispatch_model to accelerate: {e}")
|
|
|
|
# Also add to transformers modules that might import it
|
|
try:
|
|
import transformers.modeling_utils
|
|
if not hasattr(transformers.modeling_utils, 'check_tied_parameters_on_same_device'):
|
|
transformers.modeling_utils.check_tied_parameters_on_same_device = check_tied_parameters_on_same_device
|
|
if not hasattr(transformers.modeling_utils, 'dispatch_model'):
|
|
transformers.modeling_utils.dispatch_model = dispatch_model
|
|
except Exception as e:
|
|
if verbose and not accelerate_detected:
|
|
print(f" ⚠️ Could not add to transformers.modeling_utils: {e}")
|
|
|
|
# Add to global namespace as well (for direct imports)
|
|
try:
|
|
import builtins
|
|
builtins.check_tied_parameters_on_same_device = check_tied_parameters_on_same_device
|
|
builtins.dispatch_model = dispatch_model
|
|
except Exception as e:
|
|
if verbose and not accelerate_detected:
|
|
print(f" ⚠️ Could not add to global namespace: {e}")
|
|
|
|
# Add to any vibevoice modules that might need it
|
|
try:
|
|
import sys
|
|
for module_name, module in sys.modules.items():
|
|
if 'vibevoice' in module_name.lower() and hasattr(module, '__dict__'):
|
|
if 'check_tied_parameters_on_same_device' not in module.__dict__:
|
|
module.__dict__['check_tied_parameters_on_same_device'] = check_tied_parameters_on_same_device
|
|
if 'dispatch_model' not in module.__dict__:
|
|
module.__dict__['dispatch_model'] = dispatch_model
|
|
except Exception as e:
|
|
if verbose and not accelerate_detected:
|
|
print(f" ⚠️ Could not add to vibevoice modules: {e}")
|
|
|
|
cls._patches_applied.add("accelerate_compatibility")
|
|
|
|
# Only log if patches were actually needed
|
|
if verbose and not accelerate_detected:
|
|
print(" ✅ Accelerate compatibility patches applied")
|
|
elif verbose:
|
|
# Silent for working installations - no log pollution
|
|
pass
|
|
|
|
except ImportError as e:
|
|
if verbose:
|
|
print(f" ❌ accelerate not available: {e}")
|
|
except Exception as e:
|
|
warnings.warn(f"Accelerate compatibility patching failed: {e}")
|
|
|
|
@classmethod
|
|
def patch_step_audio_tokenization(cls, verbose: bool = True):
|
|
"""
|
|
Patch Step Audio EditX tokenization bug in transformers 4.54+
|
|
|
|
Issue: transformers 4.54+ changed tokenization behavior causing Step Audio EditX
|
|
to generate silent audio. The tokenizer in 4.54+ doesn't properly handle
|
|
special audio tokens (<audio_XXXXX>) which should be single tokens but get
|
|
split into multiple tokens.
|
|
|
|
Root cause: Tokenizer changes in transformers 4.54+ affect how special tokens are processed
|
|
|
|
Solution: Monkey-patch tokenizer encode/decode methods to handle audio tokens correctly
|
|
|
|
Affects: Step Audio EditX TTS engine only
|
|
"""
|
|
if "step_audio_tokenization" in cls._patches_applied:
|
|
return
|
|
|
|
try:
|
|
import transformers
|
|
|
|
# Only patch transformers 4.54+
|
|
transformers_version = tuple(map(int, transformers.__version__.split('.')[:2]))
|
|
if transformers_version < (4, 54):
|
|
return
|
|
|
|
|
|
# The bug: transformers 4.54+ doesn't recognize audio tokens (<audio_65536> through <audio_74752>)
|
|
# as special tokens, causing them to be split into multiple tokens instead of single token IDs.
|
|
#
|
|
# Root cause: Step Audio EditX model doesn't have an added_tokens.json file, and transformers 4.54+
|
|
# is stricter about requiring explicit special token registration.
|
|
#
|
|
# Fix: Monkey-patch AutoTokenizer.from_pretrained to automatically add audio tokens as special tokens
|
|
# when loading Step Audio EditX models.
|
|
|
|
from transformers import AutoTokenizer
|
|
_original_from_pretrained = AutoTokenizer.from_pretrained
|
|
|
|
def _patched_from_pretrained(pretrained_model_name_or_path, *args, **kwargs):
|
|
"""Wrapper that marks Step Audio EditX audio tokens as special tokens"""
|
|
tokenizer = _original_from_pretrained(pretrained_model_name_or_path, *args, **kwargs)
|
|
|
|
# Check if this is a Step Audio EditX model (has config with audio token range)
|
|
try:
|
|
config_path = pretrained_model_name_or_path if isinstance(pretrained_model_name_or_path, str) else None
|
|
if config_path and ('step' in config_path.lower() or 'audio' in config_path.lower()):
|
|
# Check if tokenizer has the expected vocab size for Step Audio EditX
|
|
if hasattr(tokenizer, 'vocab_size') and tokenizer.vocab_size >= 74752:
|
|
# Check if tokens are already marked
|
|
if not hasattr(tokenizer, '_step_audio_tokens_marked'):
|
|
# Mark audio token IDs (65536-74752) as special tokens
|
|
# This prevents them from being split during encoding
|
|
if hasattr(tokenizer, 'all_special_ids'):
|
|
# Add audio token IDs to the set of special token IDs
|
|
audio_token_ids = set(range(65536, 74753))
|
|
tokenizer.all_special_ids = sorted(set(tokenizer.all_special_ids) | audio_token_ids)
|
|
|
|
# Also mark in the tokenizer's internal structures
|
|
if hasattr(tokenizer, '_tokenizer') and hasattr(tokenizer._tokenizer, 'token_to_id'):
|
|
# For fast tokenizers, mark tokens in the underlying tokenizer
|
|
for i in range(65536, 74753):
|
|
token_str = f"<audio_{i}>"
|
|
# These tokens should already exist in vocab, just mark them as special
|
|
if hasattr(tokenizer._tokenizer, 'add_special_tokens'):
|
|
try:
|
|
tokenizer._tokenizer.add_special_tokens([token_str])
|
|
except:
|
|
pass # May already be marked or method not available
|
|
|
|
tokenizer._step_audio_tokens_marked = True
|
|
except Exception as e:
|
|
# Silently fail - this is a best-effort patch
|
|
pass
|
|
|
|
return tokenizer
|
|
|
|
# Apply the monkey-patch
|
|
AutoTokenizer.from_pretrained = _patched_from_pretrained
|
|
|
|
cls._patches_applied.add("step_audio_tokenization")
|
|
|
|
except ImportError as e:
|
|
if verbose:
|
|
print(f" ❌ transformers not available: {e}")
|
|
except Exception as e:
|
|
warnings.warn(f"Step Audio EditX tokenization patching failed: {e}")
|
|
|
|
# REMOVED: patch_qwen3_tts_check_model_inputs
|
|
# This patch is no longer needed - the @check_model_inputs() decorator has been
|
|
# removed directly from the bundled Qwen3-TTS code in:
|
|
# engines/qwen3_tts/impl/qwen_tts/core/tokenizer_12hz/modeling_qwen3_tts_tokenizer_v2.py
|
|
|
|
@classmethod
|
|
def get_applied_patches(cls):
|
|
"""Get list of applied patches"""
|
|
return list(cls._patches_applied)
|
|
|
|
@classmethod
|
|
def is_patch_applied(cls, patch_name: str) -> bool:
|
|
"""Check if a specific patch has been applied"""
|
|
return patch_name in cls._patches_applied
|
|
|
|
|
|
# Convenience function for easy import
|
|
def apply_transformers_patches(verbose: bool = True):
|
|
"""Apply all transformers compatibility patches"""
|
|
TransformersPatches.apply_all_patches(verbose=verbose)
|
|
|
|
|
|
# Auto-apply on import for critical patches - DISABLED
|
|
# These patches are no longer needed with transformers 4.51.3+ and cause compatibility issues
|
|
if False: # Completely disabled - was causing DynamicCache errors with newer transformers
|
|
# Only apply critical patches on import to avoid side effects
|
|
TransformersPatches.patch_flash_attention_kwargs(verbose=False)
|
|
TransformersPatches.patch_base_streamer(verbose=False) |