diff --git a/unsloth/__init__.py b/unsloth/__init__.py index b068d6a5fc..a505e89ad4 100644 --- a/unsloth/__init__.py +++ b/unsloth/__init__.py @@ -28,16 +28,19 @@ already_imported = [mod for mod in critical_modules if mod in sys.modules] from .import_fixes import ( fix_message_factory_issue, check_fbgemm_gpu_version, + disable_broken_causal_conv1d, torchvision_compatibility_check, fix_diffusers_warnings, fix_huggingface_hub, ) +disable_broken_causal_conv1d() fix_message_factory_issue() check_fbgemm_gpu_version() torchvision_compatibility_check() fix_diffusers_warnings() fix_huggingface_hub() +del disable_broken_causal_conv1d del fix_message_factory_issue del check_fbgemm_gpu_version del torchvision_compatibility_check diff --git a/unsloth/import_fixes.py b/unsloth/import_fixes.py index 4a2211f9a0..8d00b793c9 100644 --- a/unsloth/import_fixes.py +++ b/unsloth/import_fixes.py @@ -13,6 +13,8 @@ # limitations under the License. import os +import importlib.abc +import importlib.machinery import importlib.util from pathlib import Path from importlib.metadata import version as importlib_version @@ -21,6 +23,7 @@ import re import logging import textwrap import warnings +import sys # We cannot do from unsloth_zoo.log import logger since FBGEMM might cause seg faults. UNSLOTH_ENABLE_LOGGING = os.environ.get("UNSLOTH_ENABLE_LOGGING", "0") in ( @@ -1163,3 +1166,157 @@ def disable_torchcodec_if_broken(): tf_import_utils._torchcodec_available = False except (ImportError, AttributeError): pass + + +CAUSAL_CONV1D_BROKEN = False +_CAUSAL_CONV1D_PREFIX = "causal_conv1d" +_CAUSAL_CONV1D_BLOCKER_SENTINEL = "_unsloth_causal_conv1d_blocker" + + +def _is_causal_conv1d_name(module_name: str) -> bool: + return module_name == _CAUSAL_CONV1D_PREFIX or module_name.startswith( + _CAUSAL_CONV1D_PREFIX + "." + ) + + +def _resolve_module_name(module_name, package): + if not isinstance(module_name, str): + return module_name + if module_name.startswith("."): + try: + return importlib.util.resolve_name(module_name, package) + except Exception: + return module_name + return module_name + + +def _is_broken_causal_conv1d_error(error) -> bool: + checked = set() + current = error + while current is not None and id(current) not in checked: + checked.add(id(current)) + message = str(current).lower() + if ( + ("causal_conv1d_cuda" in message and "undefined symbol" in message) + or ("_zn3c103hip28c10_hip_check_implementation" in message) + or ("causal_conv1d" in message and "undefined symbol" in message) + ): + return True + current = getattr(current, "__cause__", None) or getattr( + current, "__context__", None + ) + return False + + +class _CausalConv1dImportBlockerLoader(importlib.abc.Loader): + __slots__ = ("module_name",) + + def __init__(self, module_name): + self.module_name = module_name + + def create_module(self, spec): + return None + + def exec_module(self, module): + raise ModuleNotFoundError(f"No module named '{self.module_name}'") + + +class _CausalConv1dImportBlockerFinder(importlib.abc.MetaPathFinder): + __slots__ = (_CAUSAL_CONV1D_BLOCKER_SENTINEL,) + + def __init__(self): + setattr(self, _CAUSAL_CONV1D_BLOCKER_SENTINEL, True) + + def find_spec(self, fullname, path = None, target = None): + if not CAUSAL_CONV1D_BROKEN or not _is_causal_conv1d_name(fullname): + return None + return importlib.machinery.ModuleSpec( + name = fullname, + loader = _CausalConv1dImportBlockerLoader(fullname), + is_package = fullname == _CAUSAL_CONV1D_PREFIX, + ) + + +def _patch_find_spec_for_causal_conv1d(): + current_find_spec = importlib.util.find_spec + if getattr(current_find_spec, "_unsloth_causal_conv1d_find_spec_patch", False): + return + + def _blocked_find_spec(name, package = None): + resolved_name = _resolve_module_name(name, package) + if CAUSAL_CONV1D_BROKEN and isinstance(resolved_name, str): + if _is_causal_conv1d_name(resolved_name): + return None + return current_find_spec(name, package) + + _blocked_find_spec._unsloth_causal_conv1d_find_spec_patch = True + _blocked_find_spec._unsloth_original_find_spec = current_find_spec + importlib.util.find_spec = _blocked_find_spec + + +def _install_causal_conv1d_blocker(): + _patch_find_spec_for_causal_conv1d() + for finder in sys.meta_path: + if getattr(finder, _CAUSAL_CONV1D_BLOCKER_SENTINEL, False): + return + sys.meta_path.insert(0, _CausalConv1dImportBlockerFinder()) + + +def _clear_causal_conv1d_modules(): + for module_name in list(sys.modules): + if _is_causal_conv1d_name(module_name): + sys.modules.pop(module_name, None) + + +def _disable_transformers_causal_conv1d(): + try: + import transformers.utils.import_utils as tf_import_utils + except Exception: + return + + if hasattr(tf_import_utils, "is_causal_conv1d_available"): + tf_import_utils.is_causal_conv1d_available = lambda: False + + for attr_name in ( + "_causal_conv1d_available", + "_is_causal_conv1d_available", + ): + if hasattr(tf_import_utils, attr_name): + setattr(tf_import_utils, attr_name, False) + + +def disable_broken_causal_conv1d(): + """Disable causal_conv1d dynamically when its shared library is ABI-broken. + + This mirrors Unsloth's FlashAttention fallback behavior: if importing causal_conv1d + fails with a known binary symbol error, we disable it at startup so model imports do + not hard-fail. + """ + global CAUSAL_CONV1D_BROKEN + if CAUSAL_CONV1D_BROKEN: + _install_causal_conv1d_blocker() + _disable_transformers_causal_conv1d() + return + + try: + if importlib.util.find_spec("causal_conv1d") is None: + return + except Exception: + return + + try: + import causal_conv1d # noqa: F401 + + return + except Exception as error: + if not _is_broken_causal_conv1d_error(error): + return + + CAUSAL_CONV1D_BROKEN = True + _clear_causal_conv1d_modules() + _install_causal_conv1d_blocker() + _disable_transformers_causal_conv1d() + print( + "Unsloth: Detected broken causal_conv1d binary; " + "disabling causal_conv1d fast path and continuing import." + ) diff --git a/unsloth/models/_utils.py b/unsloth/models/_utils.py index 70d71b3e06..9c4e8742d2 100644 --- a/unsloth/models/_utils.py +++ b/unsloth/models/_utils.py @@ -845,8 +845,7 @@ if DEVICE_TYPE == "cuda": "Unsloth: Your Flash Attention 2 installation seems to be broken?\n" "A possible explanation is you have a new CUDA version which isn't\n" "yet compatible with FA2? Please file a ticket to Unsloth or FA2.\n" - "We shall now use Xformers instead, which does not have any performance hits!\n" - "We found this negligible impact by benchmarking on 1x A100." + "We shall now use Xformers instead, which does not have any performance hits!" ) # Stop Flash Attention from importing! @@ -897,8 +896,7 @@ elif DEVICE_TYPE == "hip": "Unsloth: Your Flash Attention 2 installation seems to be broken?\n" "A possible explanation is you have a new CUDA version which isn't\n" "yet compatible with FA2? Please file a ticket to Unsloth or FA2.\n" - "We shall now use Xformers instead, which does not have any performance hits!\n" - "We found this negligible impact by benchmarking on 1x A100." + "We shall now use Xformers instead, which does not have any performance hits!" ) # Stop Flash Attention from importing!