From 4e69c0e415ba03a043db1fee5be8cc982239a77a Mon Sep 17 00:00:00 2001 From: Roland Tannous Date: Tue, 17 Mar 2026 18:38:55 +0000 Subject: [PATCH] fix: use venv_t5_root() so .venv_t5 respects UNSLOTH_STUDIO_HOME --- studio/backend/core/export/worker.py | 27 +++++++++++++++++--- studio/backend/core/inference/worker.py | 27 +++++++++++++++++--- studio/backend/core/training/worker.py | 27 +++++++++++++++++--- studio/backend/utils/models/model_config.py | 3 ++- studio/backend/utils/paths/__init__.py | 2 ++ studio/backend/utils/paths/storage_roots.py | 5 ++++ studio/backend/utils/transformers_version.py | 3 ++- studio/setup.sh | 1 - 8 files changed, 83 insertions(+), 12 deletions(-) diff --git a/studio/backend/core/export/worker.py b/studio/backend/core/export/worker.py index 6af6ff1193..ff0e944e17 100644 --- a/studio/backend/core/export/worker.py +++ b/studio/backend/core/export/worker.py @@ -49,9 +49,30 @@ def _activate_transformers_version(model_name: str) -> None: resolved = _resolve_base_model(model_name) if needs_transformers_5(resolved): - if not _ensure_venv_t5_exists(): - raise RuntimeError( - f"Cannot activate transformers 5.x: .venv_t5 missing at {_VENV_T5_DIR}" + from utils.paths.storage_roots import venv_t5_root + venv_t5 = str(venv_t5_root()) + if os.path.isdir(venv_t5): + sys.path.insert(0, venv_t5) + logger.info("Activated transformers 5.x from %s", venv_t5) + else: + # Fallback: pip install at runtime (slower, ~10-15s) + logger.warning(".venv_t5 not found at %s — installing at runtime", venv_t5) + import subprocess as sp + + os.makedirs(venv_t5, exist_ok = True) + r1 = sp.run( + [ + sys.executable, + "-m", + "pip", + "install", + "--target", + venv_t5, + "--no-deps", + "transformers==5.3.0", + ], + stdout = sp.PIPE, + stderr = sp.STDOUT, ) if _VENV_T5_DIR not in sys.path: sys.path.insert(0, _VENV_T5_DIR) diff --git a/studio/backend/core/inference/worker.py b/studio/backend/core/inference/worker.py index 2eb46f3217..91e197c896 100644 --- a/studio/backend/core/inference/worker.py +++ b/studio/backend/core/inference/worker.py @@ -51,9 +51,30 @@ def _activate_transformers_version(model_name: str) -> None: resolved = _resolve_base_model(model_name) if needs_transformers_5(resolved): - if not _ensure_venv_t5_exists(): - raise RuntimeError( - f"Cannot activate transformers 5.x: .venv_t5 missing at {_VENV_T5_DIR}" + from utils.paths.storage_roots import venv_t5_root + venv_t5 = str(venv_t5_root()) + if os.path.isdir(venv_t5): + sys.path.insert(0, venv_t5) + logger.info("Activated transformers 5.x from %s", venv_t5) + else: + # Fallback: pip install at runtime (slower, ~10-15s) + logger.warning(".venv_t5 not found at %s — installing at runtime", venv_t5) + import subprocess as sp + + os.makedirs(venv_t5, exist_ok = True) + r1 = sp.run( + [ + sys.executable, + "-m", + "pip", + "install", + "--target", + venv_t5, + "--no-deps", + "transformers==5.3.0", + ], + stdout = sp.PIPE, + stderr = sp.STDOUT, ) if _VENV_T5_DIR not in sys.path: sys.path.insert(0, _VENV_T5_DIR) diff --git a/studio/backend/core/training/worker.py b/studio/backend/core/training/worker.py index ccd805b7ac..6a0cda830a 100644 --- a/studio/backend/core/training/worker.py +++ b/studio/backend/core/training/worker.py @@ -45,9 +45,30 @@ def _activate_transformers_version(model_name: str) -> None: resolved = _resolve_base_model(model_name) if needs_transformers_5(resolved): - if not _ensure_venv_t5_exists(): - raise RuntimeError( - f"Cannot activate transformers 5.x: .venv_t5 missing at {_VENV_T5_DIR}" + from utils.paths.storage_roots import venv_t5_root + venv_t5 = str(venv_t5_root()) + if os.path.isdir(venv_t5): + sys.path.insert(0, venv_t5) + logger.info("Activated transformers 5.x from %s", venv_t5) + else: + # Fallback: pip install at runtime (slower, ~10-15s) + logger.warning(".venv_t5 not found at %s — installing at runtime", venv_t5) + import subprocess as sp + + os.makedirs(venv_t5, exist_ok = True) + r1 = sp.run( + [ + sys.executable, + "-m", + "pip", + "install", + "--target", + venv_t5, + "--no-deps", + "transformers==5.3.0", + ], + stdout = sp.PIPE, + stderr = sp.STDOUT, ) if _VENV_T5_DIR not in sys.path: sys.path.insert(0, _VENV_T5_DIR) diff --git a/studio/backend/utils/models/model_config.py b/studio/backend/utils/models/model_config.py index 13f1b5febf..2356600b3b 100644 --- a/studio/backend/utils/models/model_config.py +++ b/studio/backend/utils/models/model_config.py @@ -427,7 +427,8 @@ _VLM_MODEL_TYPES = { } # Pre-computed .venv_t5 path and backend dir for subprocess version switching. -_VENV_T5_DIR = str(Path.home() / ".unsloth" / "studio" / ".venv_t5") +from utils.paths.storage_roots import venv_t5_root +_VENV_T5_DIR = str(venv_t5_root()) _BACKEND_DIR = str(Path(__file__).resolve().parent.parent.parent) # Inline script executed in a subprocess with transformers 5.x activated. diff --git a/studio/backend/utils/paths/__init__.py b/studio/backend/utils/paths/__init__.py index 507fb1106b..e9cc9da562 100644 --- a/studio/backend/utils/paths/__init__.py +++ b/studio/backend/utils/paths/__init__.py @@ -8,6 +8,7 @@ Path utilities for model and dataset handling from .path_utils import normalize_path, is_local_path, is_model_cached, get_cache_path from .storage_roots import ( studio_root, + venv_t5_root, assets_root, datasets_root, dataset_uploads_root, @@ -36,6 +37,7 @@ __all__ = [ "is_model_cached", "get_cache_path", "studio_root", + "venv_t5_root", "assets_root", "datasets_root", "dataset_uploads_root", diff --git a/studio/backend/utils/paths/storage_roots.py b/studio/backend/utils/paths/storage_roots.py index 288d9c04a9..caed95639d 100644 --- a/studio/backend/utils/paths/storage_roots.py +++ b/studio/backend/utils/paths/storage_roots.py @@ -15,6 +15,11 @@ def studio_root() -> Path: return Path.home() / ".unsloth" / "studio" +def venv_t5_root() -> Path: + """Pre-installed transformers 5.x directory, respects UNSLOTH_STUDIO_HOME.""" + return studio_root() / ".venv_t5" + + def cache_root() -> Path: """Central cache directory for all studio downloads (models, datasets, etc.).""" return studio_root() / "cache" diff --git a/studio/backend/utils/transformers_version.py b/studio/backend/utils/transformers_version.py index 60b43500c0..fa11a1cfb7 100644 --- a/studio/backend/utils/transformers_version.py +++ b/studio/backend/utils/transformers_version.py @@ -62,7 +62,8 @@ TRANSFORMERS_5_VERSION = "5.3.0" TRANSFORMERS_DEFAULT_VERSION = "4.57.6" # Pre-installed directory for transformers 5.x — created by setup.sh / setup.ps1 -_VENV_T5_DIR = str(Path.home() / ".unsloth" / "studio" / ".venv_t5") +from utils.paths.storage_roots import venv_t5_root +_VENV_T5_DIR = str(venv_t5_root()) def _resolve_base_model(model_name: str) -> str: diff --git a/studio/setup.sh b/studio/setup.sh index a039fc0bfc..3e56aebf38 100755 --- a/studio/setup.sh +++ b/studio/setup.sh @@ -251,7 +251,6 @@ install_python_stack() { python "$SCRIPT_DIR/install_python_stack.py" } -<<<<<<< HEAD # Create venv under ~/.unsloth/studio/ (shared location, not in repo). # All platforms (including Colab) use the same isolated venv so that # studio dependencies are never installed into the system Python.