studio: tighten sys.prefix inference + Tauri env handling + llama.cpp env
Cycle 3 reviewer.py findings (3 P1s converging): * sys.prefix inference too broad: a developer venv named 'unsloth_studio' was being treated as a custom Studio root. Narrow with an installer- sentinel check (presence of share/studio.conf or bin/unsloth shim inside the parent dir) in both unsloth_cli/commands/studio.py and studio/backend/utils/paths/storage_roots.py. * Tauri studio/src-tauri/src/process.rs::find_unsloth_binary() hardcoded ~/.unsloth/studio. Honor UNSLOTH_STUDIO_HOME / STUDIO_HOME (in that priority order) before falling back to legacy. * unsloth-zoo's GGUF export binds LLAMA_CPP_DEFAULT_DIR at import time from UNSLOTH_LLAMA_CPP_PATH. For env-override installs, persist UNSLOTH_LLAMA_CPP_PATH alongside UNSLOTH_STUDIO_HOME in studio.conf (Unix), in the generated PowerShell launcher (Windows), and via os.environ.setdefault in the Python CLI when running on a custom root, so GGUF export uses the custom-root llama.cpp build instead of the legacy ~/.unsloth/llama.cpp. Default behaviour unchanged: no env vars are written to studio.conf in default mode, no LLAMA_CPP_PATH is set, and the dev-venv inference falls through to legacy when no installer sentinels are present.
This commit is contained in:
parent
554377bec8
commit
42f3adb2f6
5 changed files with 51 additions and 8 deletions
|
|
@ -423,7 +423,8 @@ function Install-UnslothStudio {
|
|||
# empty string here so behavior matches today exactly.
|
||||
$studioHomeExport = if ($StudioRedirectMode -eq 'env') {
|
||||
$_sq = $StudioHome -replace "'", "''"
|
||||
"`$env:UNSLOTH_STUDIO_HOME = '$_sq'`n"
|
||||
$_llama = (Join-Path $StudioHome "llama.cpp") -replace "'", "''"
|
||||
"`$env:UNSLOTH_STUDIO_HOME = '$_sq'`n`$env:UNSLOTH_LLAMA_CPP_PATH = '$_llama'`n"
|
||||
} else { "" }
|
||||
|
||||
$launcherContent = @"
|
||||
|
|
|
|||
|
|
@ -606,11 +606,15 @@ LAUNCHER_EOF
|
|||
printf '%s\n' "UNSLOTH_EXE='$_css_quoted_exe'"
|
||||
# Persist UNSLOTH_STUDIO_HOME for env-override installs so the launcher,
|
||||
# CLI, and backend pick up the same root in fresh shells where the user
|
||||
# did not re-export the env var. Default installs do NOT get this line so
|
||||
# the legacy ~/.unsloth/studio resolution is fully preserved.
|
||||
# did not re-export the env var. Also persist UNSLOTH_LLAMA_CPP_PATH so
|
||||
# unsloth-zoo's import-time LLAMA_CPP_DEFAULT_DIR binding finds the
|
||||
# custom-root build. Default installs do NOT get these lines so the
|
||||
# legacy ~/.unsloth/studio + ~/.unsloth/llama.cpp resolution stands.
|
||||
if [ "$_STUDIO_HOME_REDIRECT" = "env" ]; then
|
||||
_css_quoted_home=$(printf '%s' "$STUDIO_HOME" | sed "s/'/'\\\\''/g")
|
||||
_css_quoted_llama=$(printf '%s' "$STUDIO_HOME/llama.cpp" | sed "s/'/'\\\\''/g")
|
||||
printf '%s\n' "export UNSLOTH_STUDIO_HOME='$_css_quoted_home'"
|
||||
printf '%s\n' "export UNSLOTH_LLAMA_CPP_PATH='$_css_quoted_llama'"
|
||||
fi
|
||||
} > "$_css_data_dir/studio.conf"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,17 +11,25 @@ import tempfile
|
|||
|
||||
|
||||
def _infer_studio_home_from_venv() -> Path | None:
|
||||
"""If running from the unsloth_studio venv, the parent dir is STUDIO_HOME.
|
||||
"""If running from an installer-managed unsloth_studio venv, return the
|
||||
parent dir as STUDIO_HOME.
|
||||
|
||||
Fallback for fresh shells after a custom install where the installer
|
||||
wrote to a workspace path but the user did not re-export the env var.
|
||||
Narrowed via installer-sentinel check (share/studio.conf or bin shim)
|
||||
so a developer venv that happens to be named ``unsloth_studio`` is not
|
||||
misidentified as a custom Studio root.
|
||||
"""
|
||||
try:
|
||||
prefix = Path(sys.prefix).resolve()
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
if prefix.name == "unsloth_studio":
|
||||
return prefix.parent
|
||||
if prefix.name != "unsloth_studio":
|
||||
return None
|
||||
candidate = prefix.parent
|
||||
shim_name = "unsloth.exe" if os.name == "nt" else "unsloth"
|
||||
if (candidate / "share" / "studio.conf").is_file() or (candidate / "bin" / shim_name).exists():
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -111,9 +111,22 @@ fn find_unsloth_binary_in_studio_dir(studio: &std::path::Path) -> Option<std::pa
|
|||
}
|
||||
|
||||
pub fn find_unsloth_binary() -> Option<std::path::PathBuf> {
|
||||
// Honor UNSLOTH_STUDIO_HOME / STUDIO_HOME (in that priority order) so
|
||||
// Tauri custom-root installs can locate the venv. Falls back to the
|
||||
// legacy ~/.unsloth/studio when neither is set.
|
||||
for var in ["UNSLOTH_STUDIO_HOME", "STUDIO_HOME"] {
|
||||
if let Some(value) = std::env::var_os(var) {
|
||||
if !value.is_empty() {
|
||||
let studio = std::path::PathBuf::from(value);
|
||||
if let Some(bin) = find_unsloth_binary_in_studio_dir(&studio) {
|
||||
return Some(bin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let home = dirs::home_dir()?;
|
||||
let studio = home.join(".unsloth").join("studio");
|
||||
|
||||
find_unsloth_binary_in_studio_dir(&studio)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,18 @@ studio_app = typer.Typer(help = "Unsloth Studio commands.")
|
|||
# Returns (path, is_custom): is_custom=True only when the resolved root is a
|
||||
# real override; we use this to decide whether to re-export the env var to
|
||||
# child processes.
|
||||
def _looks_like_installer_managed_studio_home(candidate: Path) -> bool:
|
||||
"""Heuristic: only treat a directory as an installer-managed Studio root
|
||||
if it carries installer-written sentinels (studio.conf or the bin shim).
|
||||
Avoids over-matching on a dev venv that happens to be named unsloth_studio.
|
||||
"""
|
||||
shim_name = "unsloth.exe" if platform.system() == "Windows" else "unsloth"
|
||||
return (
|
||||
(candidate / "share" / "studio.conf").is_file()
|
||||
or (candidate / "bin" / shim_name).exists()
|
||||
)
|
||||
|
||||
|
||||
def _resolve_studio_home() -> tuple[Path, bool]:
|
||||
override = os.environ.get("UNSLOTH_STUDIO_HOME") or os.environ.get("STUDIO_HOME")
|
||||
if override:
|
||||
|
|
@ -37,7 +49,8 @@ def _resolve_studio_home() -> tuple[Path, bool]:
|
|||
if prefix.name == "unsloth_studio":
|
||||
inferred = prefix.parent
|
||||
legacy = (Path.home() / ".unsloth" / "studio").resolve()
|
||||
return inferred, inferred != legacy
|
||||
if inferred != legacy and _looks_like_installer_managed_studio_home(inferred):
|
||||
return inferred, True
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
return Path.home() / ".unsloth" / "studio", False
|
||||
|
|
@ -48,8 +61,12 @@ STUDIO_HOME, _STUDIO_HOME_IS_CUSTOM = _resolve_studio_home()
|
|||
# backend run.py) inherit it. Default installs MUST NOT re-export the env
|
||||
# var, because setup.sh / setup.ps1 treat its presence as "env-override
|
||||
# mode" and would relocate llama.cpp / DATA_DIR away from legacy paths.
|
||||
# UNSLOTH_LLAMA_CPP_PATH is also setdefault for custom roots so
|
||||
# unsloth-zoo's import-time LLAMA_CPP_DEFAULT_DIR binding picks up the
|
||||
# correct build dir for GGUF export.
|
||||
if _STUDIO_HOME_IS_CUSTOM:
|
||||
os.environ.setdefault("UNSLOTH_STUDIO_HOME", str(STUDIO_HOME))
|
||||
os.environ.setdefault("UNSLOTH_LLAMA_CPP_PATH", str(STUDIO_HOME / "llama.cpp"))
|
||||
BOOTSTRAP_PASSWORD_FILE = ".bootstrap_password"
|
||||
DESKTOP_SECRET_FILE = ".desktop_secret"
|
||||
DEFAULT_ADMIN_USERNAME = "unsloth"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue