Fix/llama.cppbuilding (#4804)
* Simplify llama.cpp install logic * print release tag * Retry failed json decode * don't pull all ggml releases * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove test file changes from main PR Test changes for test_pr4562_bugfixes.py will be submitted in a separate PR to keep this PR focused on the install path simplification. * Fix setup.sh executable bit and direct tag lookup for pinned releases - Restore setup.sh file mode to 100755 (was accidentally changed to 100644) - Add direct GitHub API tag lookup in iter_release_payloads_by_time for non-latest requested tags (e.g. b7879) instead of relying on paginated release scans that may miss older releases beyond the 5-page limit - Update stale DEFAULT_PUBLISHED_REPO comment to match new value * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix force-compile default ref and remove dead code in setup.ps1 - Change FORCE_COMPILE_DEFAULT_REF from "main" to "master" in all three files (install_llama_prebuilt.py, setup.sh, setup.ps1) since ggml-org/llama.cpp uses "master" as its default branch, not "main". Using "main" would cause git clone --branch to fail when UNSLOTH_LLAMA_FORCE_COMPILE=1 with UNSLOTH_LLAMA_TAG=latest. - Remove dead if ($SkipPrebuiltInstall) block inside the else branch of setup.ps1 that could never be reached (the outer elseif already handles $SkipPrebuiltInstall=true). - Maintain setup.sh executable bit (100755). * Improve iter_release_payloads_by_time error handling for direct tag lookup When a pinned release tag is not found (HTTP 404), fall through to the paginated release scan instead of silently returning empty results. Non-404 errors (network failures, rate limits) are propagated to the caller so users get actionable error messages. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
parent
c1685b9459
commit
ac562bac66
3 changed files with 709 additions and 290 deletions
|
|
@ -65,9 +65,10 @@ def env_int(name: str, default: int, *, minimum: int | None = None) -> int:
|
|||
# errors. Only use "master" temporarily when the latest release is missing
|
||||
# support for a new model architecture.
|
||||
DEFAULT_LLAMA_TAG = os.environ.get("UNSLOTH_LLAMA_TAG", "latest")
|
||||
# Force all installs to use mainline llama.cpp from ggml-org.
|
||||
# Previously: DEFAULT_PUBLISHED_REPO = os.environ.get("UNSLOTH_LLAMA_RELEASE_REPO", "unslothai/llama.cpp")
|
||||
DEFAULT_PUBLISHED_REPO = "ggml-org/llama.cpp"
|
||||
# Default published repo for prebuilt release resolution. Linux uses
|
||||
# Unsloth prebuilts; setup.sh/setup.ps1 pass --published-repo explicitly
|
||||
# for macOS/Windows to override with ggml-org/llama.cpp when needed.
|
||||
DEFAULT_PUBLISHED_REPO = "unslothai/llama.cpp"
|
||||
DEFAULT_PUBLISHED_TAG = os.environ.get("UNSLOTH_LLAMA_RELEASE_TAG")
|
||||
DEFAULT_PUBLISHED_MANIFEST_ASSET = os.environ.get(
|
||||
"UNSLOTH_LLAMA_RELEASE_MANIFEST_ASSET", "llama-prebuilt-manifest.json"
|
||||
|
|
@ -89,6 +90,12 @@ GITHUB_AUTH_HOSTS = {"api.github.com", "github.com"}
|
|||
RETRYABLE_HTTP_STATUS = {408, 429, 500, 502, 503, 504}
|
||||
HTTP_FETCH_ATTEMPTS = 4
|
||||
HTTP_FETCH_BASE_DELAY_SECONDS = 0.75
|
||||
JSON_FETCH_ATTEMPTS = 3
|
||||
DEFAULT_GITHUB_RELEASE_SCAN_MAX_PAGES = env_int(
|
||||
"UNSLOTH_LLAMA_GITHUB_RELEASE_SCAN_MAX_PAGES",
|
||||
5,
|
||||
minimum = 1,
|
||||
)
|
||||
SERVER_PORT_BIND_ATTEMPTS = 3
|
||||
SERVER_BIND_RETRY_WINDOW_SECONDS = 5.0
|
||||
TTY_PROGRESS_START_DELAY_SECONDS = 0.5
|
||||
|
|
@ -97,6 +104,58 @@ DEFAULT_MAX_PREBUILT_RELEASE_FALLBACKS = env_int(
|
|||
2,
|
||||
minimum = 1,
|
||||
)
|
||||
FORCE_COMPILE_DEFAULT_REF = os.environ.get("UNSLOTH_LLAMA_FORCE_COMPILE_REF", "master")
|
||||
|
||||
DIRECT_LINUX_BUNDLE_PROFILES: dict[str, dict[str, Any]] = {
|
||||
"cuda12-older": {
|
||||
"runtime_line": "cuda12",
|
||||
"coverage_class": "older",
|
||||
"supported_sms": ["70", "75", "80", "86", "89"],
|
||||
"min_sm": 70,
|
||||
"max_sm": 89,
|
||||
"rank": 10,
|
||||
},
|
||||
"cuda12-newer": {
|
||||
"runtime_line": "cuda12",
|
||||
"coverage_class": "newer",
|
||||
"supported_sms": ["86", "89", "90", "100", "120"],
|
||||
"min_sm": 86,
|
||||
"max_sm": 120,
|
||||
"rank": 20,
|
||||
},
|
||||
"cuda12-portable": {
|
||||
"runtime_line": "cuda12",
|
||||
"coverage_class": "portable",
|
||||
"supported_sms": ["70", "75", "80", "86", "89", "90", "100", "120"],
|
||||
"min_sm": 70,
|
||||
"max_sm": 120,
|
||||
"rank": 30,
|
||||
},
|
||||
"cuda13-older": {
|
||||
"runtime_line": "cuda13",
|
||||
"coverage_class": "older",
|
||||
"supported_sms": ["75", "80", "86", "89"],
|
||||
"min_sm": 75,
|
||||
"max_sm": 89,
|
||||
"rank": 40,
|
||||
},
|
||||
"cuda13-newer": {
|
||||
"runtime_line": "cuda13",
|
||||
"coverage_class": "newer",
|
||||
"supported_sms": ["86", "89", "90", "100", "120"],
|
||||
"min_sm": 86,
|
||||
"max_sm": 120,
|
||||
"rank": 50,
|
||||
},
|
||||
"cuda13-portable": {
|
||||
"runtime_line": "cuda13",
|
||||
"coverage_class": "portable",
|
||||
"supported_sms": ["75", "80", "86", "89", "90", "100", "120"],
|
||||
"min_sm": 75,
|
||||
"max_sm": 120,
|
||||
"rank": 60,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -753,32 +812,48 @@ def download_bytes(
|
|||
|
||||
|
||||
def fetch_json(url: str) -> Any:
|
||||
try:
|
||||
data = download_bytes(
|
||||
url,
|
||||
timeout = 30,
|
||||
headers = github_api_headers(url)
|
||||
if is_github_api_url(url)
|
||||
else auth_headers(url),
|
||||
)
|
||||
except urllib.error.HTTPError as exc:
|
||||
if exc.code == 403 and is_github_api_url(url):
|
||||
hint = ""
|
||||
if not (os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN")):
|
||||
hint = "; set GH_TOKEN or GITHUB_TOKEN to avoid GitHub API rate limits"
|
||||
raise RuntimeError(f"GitHub API returned 403 for {url}{hint}") from exc
|
||||
raise
|
||||
if not data:
|
||||
raise RuntimeError(f"downloaded empty JSON payload from {url}")
|
||||
try:
|
||||
payload = json.loads(data.decode("utf-8"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
||||
raise RuntimeError(f"downloaded invalid JSON from {url}: {exc}") from exc
|
||||
if not isinstance(payload, dict) and not isinstance(payload, list):
|
||||
raise RuntimeError(
|
||||
f"downloaded unexpected JSON type from {url}: {type(payload).__name__}"
|
||||
)
|
||||
return payload
|
||||
attempts = JSON_FETCH_ATTEMPTS if is_github_api_url(url) else 1
|
||||
last_decode_exc: Exception | None = None
|
||||
for attempt in range(1, attempts + 1):
|
||||
try:
|
||||
data = download_bytes(
|
||||
url,
|
||||
timeout = 30,
|
||||
headers = github_api_headers(url)
|
||||
if is_github_api_url(url)
|
||||
else auth_headers(url),
|
||||
)
|
||||
except urllib.error.HTTPError as exc:
|
||||
if exc.code == 403 and is_github_api_url(url):
|
||||
hint = ""
|
||||
if not (os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN")):
|
||||
hint = (
|
||||
"; set GH_TOKEN or GITHUB_TOKEN to avoid GitHub API rate limits"
|
||||
)
|
||||
raise RuntimeError(f"GitHub API returned 403 for {url}{hint}") from exc
|
||||
raise
|
||||
if not data:
|
||||
last_decode_exc = RuntimeError(f"downloaded empty JSON payload from {url}")
|
||||
else:
|
||||
try:
|
||||
payload = json.loads(data.decode("utf-8"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
||||
last_decode_exc = RuntimeError(
|
||||
f"downloaded invalid JSON from {url}: {exc}"
|
||||
)
|
||||
else:
|
||||
if not isinstance(payload, dict) and not isinstance(payload, list):
|
||||
raise RuntimeError(
|
||||
f"downloaded unexpected JSON type from {url}: {type(payload).__name__}"
|
||||
)
|
||||
return payload
|
||||
if attempt >= attempts:
|
||||
assert last_decode_exc is not None
|
||||
raise last_decode_exc
|
||||
log(f"json fetch failed ({attempt}/{attempts}) for {url}; retrying")
|
||||
sleep_backoff(attempt)
|
||||
assert last_decode_exc is not None
|
||||
raise last_decode_exc
|
||||
|
||||
|
||||
def download_file(url: str, destination: Path) -> None:
|
||||
|
|
@ -838,12 +913,16 @@ def download_file_verified(
|
|||
url: str,
|
||||
destination: Path,
|
||||
*,
|
||||
expected_sha256: str,
|
||||
expected_sha256: str | None,
|
||||
label: str,
|
||||
) -> None:
|
||||
normalized_expected = normalize_sha256_digest(expected_sha256)
|
||||
if not normalized_expected:
|
||||
raise PrebuiltFallback(f"{label} did not have a valid approved sha256")
|
||||
download_file(url, destination)
|
||||
log(
|
||||
f"downloaded {label} without a published sha256; relying on install validation"
|
||||
)
|
||||
return
|
||||
|
||||
for attempt in range(1, 3):
|
||||
download_file(url, destination)
|
||||
|
|
@ -898,7 +977,12 @@ def github_release(repo: str, tag: str) -> dict[str, Any]:
|
|||
return payload
|
||||
|
||||
|
||||
def github_releases(repo: str, *, per_page: int = 100) -> list[dict[str, Any]]:
|
||||
def github_releases(
|
||||
repo: str,
|
||||
*,
|
||||
per_page: int = 100,
|
||||
max_pages: int = 0,
|
||||
) -> list[dict[str, Any]]:
|
||||
releases: list[dict[str, Any]] = []
|
||||
page = 1
|
||||
while True:
|
||||
|
|
@ -912,6 +996,8 @@ def github_releases(repo: str, *, per_page: int = 100) -> list[dict[str, Any]]:
|
|||
if len(payload) < per_page:
|
||||
break
|
||||
page += 1
|
||||
if max_pages > 0 and page > max_pages:
|
||||
break
|
||||
return releases
|
||||
|
||||
|
||||
|
|
@ -925,6 +1011,372 @@ def latest_upstream_release_tag() -> str:
|
|||
return tag
|
||||
|
||||
|
||||
def is_release_tag_like(value: str | None) -> bool:
|
||||
return isinstance(value, str) and bool(re.fullmatch(r"b\d+", value.strip()))
|
||||
|
||||
|
||||
def release_time_sort_key(release: dict[str, Any]) -> tuple[str, int]:
|
||||
published_at = release.get("published_at")
|
||||
created_at = release.get("created_at")
|
||||
release_id = release.get("id")
|
||||
timestamp = (
|
||||
published_at
|
||||
if isinstance(published_at, str) and published_at
|
||||
else created_at
|
||||
if isinstance(created_at, str) and created_at
|
||||
else ""
|
||||
)
|
||||
try:
|
||||
normalized_id = int(release_id)
|
||||
except (TypeError, ValueError):
|
||||
normalized_id = 0
|
||||
return (timestamp, normalized_id)
|
||||
|
||||
|
||||
def iter_release_payloads_by_time(
|
||||
repo: str,
|
||||
published_release_tag: str = "",
|
||||
requested_tag: str = "",
|
||||
) -> Iterable[dict[str, Any]]:
|
||||
if published_release_tag:
|
||||
yield github_release(repo, published_release_tag)
|
||||
return
|
||||
|
||||
if (
|
||||
requested_tag
|
||||
and requested_tag != "latest"
|
||||
and is_release_tag_like(requested_tag)
|
||||
):
|
||||
try:
|
||||
yield github_release(repo, requested_tag)
|
||||
return
|
||||
except urllib.error.HTTPError as exc:
|
||||
if exc.code == 404:
|
||||
log(
|
||||
f"release tag {requested_tag} not found in {repo}; scanning recent releases"
|
||||
)
|
||||
else:
|
||||
raise
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
releases = [
|
||||
release
|
||||
for release in github_releases(
|
||||
repo, max_pages = DEFAULT_GITHUB_RELEASE_SCAN_MAX_PAGES
|
||||
)
|
||||
if isinstance(release, dict)
|
||||
and not release.get("draft")
|
||||
and not release.get("prerelease")
|
||||
]
|
||||
releases.sort(key = release_time_sort_key, reverse = True)
|
||||
for release in releases:
|
||||
yield release
|
||||
|
||||
|
||||
def direct_release_matches_request(
|
||||
*, release_tag: str, llama_tag: str, requested_tag: str
|
||||
) -> bool:
|
||||
if requested_tag == "latest":
|
||||
return True
|
||||
for candidate in (release_tag, llama_tag):
|
||||
if refs_match(candidate, requested_tag):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def synthetic_checksums_for_release(
|
||||
repo: str, release_tag: str, upstream_tag: str
|
||||
) -> ApprovedReleaseChecksums:
|
||||
return ApprovedReleaseChecksums(
|
||||
repo = repo,
|
||||
release_tag = release_tag,
|
||||
upstream_tag = upstream_tag,
|
||||
artifacts = {},
|
||||
)
|
||||
|
||||
|
||||
def parse_direct_linux_release_bundle(
|
||||
repo: str, release: dict[str, Any]
|
||||
) -> PublishedReleaseBundle | None:
|
||||
release_tag = release.get("tag_name")
|
||||
if not isinstance(release_tag, str) or not release_tag:
|
||||
return None
|
||||
|
||||
assets = release_asset_map(release)
|
||||
artifacts: list[PublishedLlamaArtifact] = []
|
||||
inferred_labels: list[str] = []
|
||||
|
||||
linux_asset_re = re.compile(
|
||||
r"^app-(?P<label>.+)-(?P<target>linux-x64(?:-cpu)?|linux-x64-(?:cuda12|cuda13)-(?:older|newer|portable))\.tar\.gz$"
|
||||
)
|
||||
for asset_name in sorted(assets):
|
||||
match = linux_asset_re.fullmatch(asset_name)
|
||||
if not match:
|
||||
continue
|
||||
inferred_labels.append(match.group("label"))
|
||||
target = match.group("target")
|
||||
if target in {"linux-x64", "linux-x64-cpu"}:
|
||||
artifacts.append(
|
||||
PublishedLlamaArtifact(
|
||||
asset_name = asset_name,
|
||||
install_kind = "linux-cpu",
|
||||
runtime_line = None,
|
||||
coverage_class = None,
|
||||
supported_sms = [],
|
||||
min_sm = None,
|
||||
max_sm = None,
|
||||
bundle_profile = None,
|
||||
rank = 1000,
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
bundle_profile = target.removeprefix("linux-x64-")
|
||||
profile = DIRECT_LINUX_BUNDLE_PROFILES.get(bundle_profile)
|
||||
if profile is None:
|
||||
continue
|
||||
artifacts.append(
|
||||
PublishedLlamaArtifact(
|
||||
asset_name = asset_name,
|
||||
install_kind = "linux-cuda",
|
||||
runtime_line = str(profile["runtime_line"]),
|
||||
coverage_class = str(profile["coverage_class"]),
|
||||
supported_sms = [str(value) for value in profile["supported_sms"]],
|
||||
min_sm = int(profile["min_sm"]),
|
||||
max_sm = int(profile["max_sm"]),
|
||||
bundle_profile = bundle_profile,
|
||||
rank = int(profile["rank"]),
|
||||
)
|
||||
)
|
||||
|
||||
if not artifacts:
|
||||
return None
|
||||
|
||||
upstream_tag = (
|
||||
release_tag
|
||||
if is_release_tag_like(release_tag)
|
||||
else inferred_labels[0]
|
||||
if len(set(inferred_labels)) == 1 and inferred_labels
|
||||
else release_tag
|
||||
)
|
||||
selection_log = [
|
||||
f"published_release: repo={repo}",
|
||||
f"published_release: tag={release_tag}",
|
||||
f"published_release: upstream_tag={upstream_tag}",
|
||||
"published_release: direct_asset_scan=linux",
|
||||
]
|
||||
return PublishedReleaseBundle(
|
||||
repo = repo,
|
||||
release_tag = release_tag,
|
||||
upstream_tag = upstream_tag,
|
||||
assets = assets,
|
||||
manifest_asset_name = DEFAULT_PUBLISHED_MANIFEST_ASSET,
|
||||
artifacts = artifacts,
|
||||
selection_log = selection_log,
|
||||
)
|
||||
|
||||
|
||||
def direct_linux_release_plan(
|
||||
release: dict[str, Any],
|
||||
host: HostInfo,
|
||||
repo: str,
|
||||
requested_tag: str,
|
||||
) -> InstallReleasePlan | None:
|
||||
bundle = parse_direct_linux_release_bundle(repo, release)
|
||||
if bundle is None:
|
||||
return None
|
||||
if not direct_release_matches_request(
|
||||
release_tag = bundle.release_tag,
|
||||
llama_tag = bundle.upstream_tag,
|
||||
requested_tag = requested_tag,
|
||||
):
|
||||
return None
|
||||
|
||||
attempts: list[AssetChoice] = []
|
||||
if host.has_usable_nvidia:
|
||||
selection = linux_cuda_choice_from_release(host, bundle)
|
||||
if selection is not None:
|
||||
attempts.extend(selection.attempts)
|
||||
cpu_choice = published_asset_choice_for_kind(bundle, "linux-cpu")
|
||||
if cpu_choice is not None:
|
||||
attempts.append(cpu_choice)
|
||||
if not attempts:
|
||||
raise PrebuiltFallback("no compatible Linux prebuilt asset was found")
|
||||
return InstallReleasePlan(
|
||||
requested_tag = requested_tag,
|
||||
llama_tag = bundle.upstream_tag,
|
||||
release_tag = bundle.release_tag,
|
||||
attempts = attempts,
|
||||
approved_checksums = synthetic_checksums_for_release(
|
||||
repo,
|
||||
bundle.release_tag,
|
||||
bundle.upstream_tag,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def direct_upstream_release_plan(
|
||||
release: dict[str, Any],
|
||||
host: HostInfo,
|
||||
repo: str,
|
||||
requested_tag: str,
|
||||
) -> InstallReleasePlan | None:
|
||||
release_tag = release.get("tag_name")
|
||||
if not isinstance(release_tag, str) or not release_tag:
|
||||
return None
|
||||
if not direct_release_matches_request(
|
||||
release_tag = release_tag,
|
||||
llama_tag = release_tag,
|
||||
requested_tag = requested_tag,
|
||||
):
|
||||
return None
|
||||
|
||||
assets = release_asset_map(release)
|
||||
attempts: list[AssetChoice] = []
|
||||
if host.is_windows and host.is_x86_64:
|
||||
if host.has_usable_nvidia:
|
||||
torch_preference = detect_torch_cuda_runtime_preference(host)
|
||||
attempts.extend(
|
||||
windows_cuda_attempts(
|
||||
host,
|
||||
release_tag,
|
||||
assets,
|
||||
torch_preference.runtime_line,
|
||||
torch_preference.selection_log,
|
||||
)
|
||||
)
|
||||
cpu_asset = f"llama-{release_tag}-bin-win-cpu-x64.zip"
|
||||
cpu_url = assets.get(cpu_asset)
|
||||
if cpu_url:
|
||||
attempts.append(
|
||||
AssetChoice(
|
||||
repo = repo,
|
||||
tag = release_tag,
|
||||
name = cpu_asset,
|
||||
url = cpu_url,
|
||||
source_label = "upstream",
|
||||
install_kind = "windows-cpu",
|
||||
)
|
||||
)
|
||||
elif host.is_macos and host.is_arm64:
|
||||
asset_name = f"llama-{release_tag}-bin-macos-arm64.tar.gz"
|
||||
asset_url = assets.get(asset_name)
|
||||
if asset_url:
|
||||
attempts.append(
|
||||
AssetChoice(
|
||||
repo = repo,
|
||||
tag = release_tag,
|
||||
name = asset_name,
|
||||
url = asset_url,
|
||||
source_label = "upstream",
|
||||
install_kind = "macos-arm64",
|
||||
)
|
||||
)
|
||||
elif host.is_macos and host.is_x86_64:
|
||||
asset_name = f"llama-{release_tag}-bin-macos-x64.tar.gz"
|
||||
asset_url = assets.get(asset_name)
|
||||
if asset_url:
|
||||
attempts.append(
|
||||
AssetChoice(
|
||||
repo = repo,
|
||||
tag = release_tag,
|
||||
name = asset_name,
|
||||
url = asset_url,
|
||||
source_label = "upstream",
|
||||
install_kind = "macos-x64",
|
||||
)
|
||||
)
|
||||
elif host.is_linux and host.is_x86_64 and not host.has_usable_nvidia:
|
||||
asset_name = f"llama-{release_tag}-bin-ubuntu-x64.tar.gz"
|
||||
asset_url = assets.get(asset_name)
|
||||
if asset_url:
|
||||
attempts.append(
|
||||
AssetChoice(
|
||||
repo = repo,
|
||||
tag = release_tag,
|
||||
name = asset_name,
|
||||
url = asset_url,
|
||||
source_label = "upstream",
|
||||
install_kind = "linux-cpu",
|
||||
)
|
||||
)
|
||||
if not attempts:
|
||||
raise PrebuiltFallback("no compatible upstream prebuilt asset was found")
|
||||
return InstallReleasePlan(
|
||||
requested_tag = requested_tag,
|
||||
llama_tag = release_tag,
|
||||
release_tag = release_tag,
|
||||
attempts = attempts,
|
||||
approved_checksums = synthetic_checksums_for_release(
|
||||
repo,
|
||||
release_tag,
|
||||
release_tag,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def resolve_simple_install_release_plans(
|
||||
llama_tag: str,
|
||||
host: HostInfo,
|
||||
published_repo: str,
|
||||
published_release_tag: str,
|
||||
*,
|
||||
max_release_fallbacks: int = DEFAULT_MAX_PREBUILT_RELEASE_FALLBACKS,
|
||||
) -> tuple[str, list[InstallReleasePlan]]:
|
||||
repo = published_repo or DEFAULT_PUBLISHED_REPO
|
||||
requested_tag = normalized_requested_llama_tag(llama_tag)
|
||||
allow_older_release_fallback = (
|
||||
requested_tag == "latest" and not published_release_tag
|
||||
)
|
||||
release_limit = max(1, max_release_fallbacks)
|
||||
plans: list[InstallReleasePlan] = []
|
||||
last_error: PrebuiltFallback | None = None
|
||||
|
||||
try:
|
||||
releases = iter_release_payloads_by_time(
|
||||
repo, published_release_tag, requested_tag
|
||||
)
|
||||
for release in releases:
|
||||
try:
|
||||
if host.is_linux and repo == "unslothai/llama.cpp":
|
||||
plan = direct_linux_release_plan(release, host, repo, requested_tag)
|
||||
else:
|
||||
plan = direct_upstream_release_plan(
|
||||
release, host, repo, requested_tag
|
||||
)
|
||||
if plan is None:
|
||||
continue
|
||||
except PrebuiltFallback as exc:
|
||||
last_error = exc
|
||||
if not allow_older_release_fallback:
|
||||
raise
|
||||
release_tag = release.get("tag_name") or "unknown"
|
||||
log(
|
||||
"published release skipped for install planning: "
|
||||
f"{repo}@{release_tag} ({exc})"
|
||||
)
|
||||
continue
|
||||
|
||||
plans.append(plan)
|
||||
if not allow_older_release_fallback or len(plans) >= release_limit:
|
||||
break
|
||||
except PrebuiltFallback:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise PrebuiltFallback(
|
||||
f"failed to inspect published releases in {repo}: {exc}"
|
||||
) from exc
|
||||
|
||||
if plans:
|
||||
return requested_tag, plans
|
||||
if last_error is not None:
|
||||
raise last_error
|
||||
raise PrebuiltFallback(
|
||||
f"no installable published llama.cpp releases were found in {repo}"
|
||||
)
|
||||
|
||||
|
||||
def normalized_requested_llama_tag(requested_tag: str | None) -> str:
|
||||
if isinstance(requested_tag, str):
|
||||
normalized = requested_tag.strip()
|
||||
|
|
@ -1435,7 +1887,7 @@ def iter_published_release_bundles(
|
|||
releases = (
|
||||
[github_release(repo, published_release_tag)]
|
||||
if published_release_tag
|
||||
else github_releases(repo)
|
||||
else github_releases(repo, max_pages = DEFAULT_GITHUB_RELEASE_SCAN_MAX_PAGES)
|
||||
)
|
||||
for release in releases:
|
||||
if not published_release_tag and (
|
||||
|
|
@ -1669,7 +2121,9 @@ def latest_published_linux_cuda_tag(host: HostInfo, published_repo: str) -> str
|
|||
|
||||
|
||||
def iter_upstream_releases() -> Iterable[dict[str, Any]]:
|
||||
for release in github_releases(UPSTREAM_REPO):
|
||||
for release in github_releases(
|
||||
UPSTREAM_REPO, max_pages = DEFAULT_GITHUB_RELEASE_SCAN_MAX_PAGES
|
||||
):
|
||||
if release.get("draft") or release.get("prerelease"):
|
||||
continue
|
||||
yield release
|
||||
|
|
@ -2799,7 +3253,7 @@ def hydrate_source_tree(
|
|||
work_dir: Path,
|
||||
*,
|
||||
source_repo: str = UPSTREAM_REPO,
|
||||
expected_sha256: str,
|
||||
expected_sha256: str | None,
|
||||
source_label: str | None = None,
|
||||
exact_source: bool = False,
|
||||
) -> None:
|
||||
|
|
@ -3231,10 +3685,6 @@ def install_from_archives(
|
|||
) -> tuple[Path, Path]:
|
||||
main_archive = work_dir / choice.name
|
||||
log(f"downloading {choice.name} from {choice.source_label} release")
|
||||
if not choice.expected_sha256:
|
||||
raise PrebuiltFallback(
|
||||
f"approved checksum was missing for selected asset {choice.name}"
|
||||
)
|
||||
download_file_verified(
|
||||
choice.url,
|
||||
main_archive,
|
||||
|
|
@ -3962,7 +4412,7 @@ def require_approved_source_hash(
|
|||
|
||||
def preferred_source_archive(
|
||||
checksums: ApprovedReleaseChecksums, llama_tag: str
|
||||
) -> tuple[str, str, ApprovedArtifactHash, bool]:
|
||||
) -> tuple[str, str, ApprovedArtifactHash | None, bool]:
|
||||
exact_source = exact_source_archive_hash(checksums)
|
||||
exact_repo = repo_slug_from_source(checksums.source_repo) or repo_slug_from_source(
|
||||
checksums.source_repo_url
|
||||
|
|
@ -3974,7 +4424,7 @@ def preferred_source_archive(
|
|||
exact_source,
|
||||
True,
|
||||
)
|
||||
legacy = require_approved_source_hash(checksums, llama_tag)
|
||||
legacy = checksums.artifacts.get(source_archive_logical_name(llama_tag))
|
||||
return (
|
||||
UPSTREAM_REPO,
|
||||
llama_tag,
|
||||
|
|
@ -3990,6 +4440,8 @@ def selected_source_archive_metadata(
|
|||
_source_repo, _source_ref, source_archive, _exact_source = preferred_source_archive(
|
||||
checksums, llama_tag
|
||||
)
|
||||
if source_archive is None:
|
||||
return source_archive_logical_name(llama_tag), None
|
||||
return source_archive.asset_name, source_archive.sha256
|
||||
|
||||
|
||||
|
|
@ -4153,8 +4605,6 @@ def expected_install_fingerprint(
|
|||
choice: AssetChoice,
|
||||
approved_checksums: ApprovedReleaseChecksums,
|
||||
) -> str | None:
|
||||
if not choice.expected_sha256:
|
||||
return None
|
||||
source_asset_name, source_sha256 = selected_source_archive_metadata(
|
||||
approved_checksums,
|
||||
llama_tag,
|
||||
|
|
@ -4352,7 +4802,7 @@ def validate_prebuilt_choice(
|
|||
install_dir,
|
||||
work_dir,
|
||||
source_repo = source_repo,
|
||||
expected_sha256 = source_archive.sha256,
|
||||
expected_sha256 = source_archive.sha256 if source_archive is not None else None,
|
||||
source_label = (
|
||||
f"llama.cpp source tree for {source_repo}@{source_ref}"
|
||||
if exact_source
|
||||
|
|
@ -4477,7 +4927,12 @@ def validate_prebuilt_attempts(
|
|||
|
||||
|
||||
def install_prebuilt(
|
||||
install_dir: Path, llama_tag: str, published_repo: str, published_release_tag: str
|
||||
install_dir: Path,
|
||||
llama_tag: str,
|
||||
published_repo: str,
|
||||
published_release_tag: str,
|
||||
*,
|
||||
simple_policy: bool = False,
|
||||
) -> None:
|
||||
host = detect_host()
|
||||
choice: AssetChoice | None = None
|
||||
|
|
@ -4491,12 +4946,20 @@ def install_prebuilt(
|
|||
log(
|
||||
f"no existing llama.cpp install detected at {install_dir}; performing fresh prebuilt install"
|
||||
)
|
||||
requested_tag, release_plans = resolve_install_release_plans(
|
||||
llama_tag,
|
||||
host,
|
||||
published_repo,
|
||||
published_release_tag,
|
||||
)
|
||||
if simple_policy:
|
||||
requested_tag, release_plans = resolve_simple_install_release_plans(
|
||||
llama_tag,
|
||||
host,
|
||||
published_repo,
|
||||
published_release_tag,
|
||||
)
|
||||
else:
|
||||
requested_tag, release_plans = resolve_install_release_plans(
|
||||
llama_tag,
|
||||
host,
|
||||
published_repo,
|
||||
published_release_tag,
|
||||
)
|
||||
if release_plans and existing_install_matches_plan(
|
||||
install_dir, host, release_plans[0]
|
||||
):
|
||||
|
|
@ -4600,6 +5063,11 @@ def parse_args() -> argparse.Namespace:
|
|||
"until a usable published llama.cpp release bundle is found."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--simple-policy",
|
||||
action = "store_true",
|
||||
help = "Use the simplified platform-specific prebuilt selection policy.",
|
||||
)
|
||||
resolve_group = parser.add_mutually_exclusive_group()
|
||||
resolve_group.add_argument(
|
||||
"--resolve-llama-tag",
|
||||
|
|
@ -4719,6 +5187,7 @@ def main() -> int:
|
|||
llama_tag = args.llama_tag,
|
||||
published_repo = args.published_repo,
|
||||
published_release_tag = args.published_release_tag or "",
|
||||
simple_policy = args.simple_policy,
|
||||
)
|
||||
return EXIT_SUCCESS
|
||||
|
||||
|
|
|
|||
160
studio/setup.ps1
160
studio/setup.ps1
|
|
@ -34,6 +34,7 @@ $PackageDir = Split-Path -Parent $ScriptDir
|
|||
$DefaultLlamaPrForce = ""
|
||||
$DefaultLlamaSource = "https://github.com/ggml-org/llama.cpp"
|
||||
$DefaultLlamaTag = "latest"
|
||||
$DefaultLlamaForceCompileRef = "master"
|
||||
|
||||
# Verbose can be enabled either by CLI flag or by UNSLOTH_VERBOSE=1.
|
||||
$script:UnslothVerbose = ($env:UNSLOTH_VERBOSE -eq '1')
|
||||
|
|
@ -81,6 +82,31 @@ function New-UnslothTemporaryFile {
|
|||
return Get-Item -LiteralPath $tempPath
|
||||
}
|
||||
|
||||
function Get-InstalledLlamaPrebuiltRelease {
|
||||
param([string]$InstallDir)
|
||||
|
||||
$metadataPath = Join-Path $InstallDir "UNSLOTH_PREBUILT_INFO.json"
|
||||
if (-not (Test-Path $metadataPath)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
try {
|
||||
$payload = Get-Content $metadataPath -Raw | ConvertFrom-Json
|
||||
} catch {
|
||||
return $null
|
||||
}
|
||||
|
||||
if (-not $payload.published_repo -or -not $payload.release_tag) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$message = "installed release: $($payload.published_repo)@$($payload.release_tag)"
|
||||
if ($payload.tag -and $payload.tag -ne $payload.release_tag) {
|
||||
$message += " (tag $($payload.tag))"
|
||||
}
|
||||
return $message
|
||||
}
|
||||
|
||||
# Find nvcc on PATH, CUDA_PATH, or standard toolkit dirs.
|
||||
# Returns the path to nvcc.exe, or $null if not found.
|
||||
function Find-Nvcc {
|
||||
|
|
@ -133,7 +159,9 @@ function Find-Nvcc {
|
|||
|
||||
# 3. Scan standard toolkit directory
|
||||
if (Test-Path $toolkitBase) {
|
||||
$latest = Get-ChildItem -Directory $toolkitBase | Sort-Object Name | Select-Object -Last 1
|
||||
$latest = Get-ChildItem -Directory $toolkitBase | Where-Object {
|
||||
$_.Name -match '^v(\d+)\.(\d+)'
|
||||
} | Sort-Object { [version]($_.Name -replace '^v','') } -Descending | Select-Object -First 1
|
||||
if ($latest -and (Test-Path (Join-Path $latest.FullName 'bin\nvcc.exe'))) {
|
||||
return (Join-Path $latest.FullName 'bin\nvcc.exe')
|
||||
}
|
||||
|
|
@ -1618,6 +1646,7 @@ if ($LlamaSource.EndsWith('.git')) { $LlamaSource = $LlamaSource.Substring(0, $L
|
|||
$ResolvedSourceUrl = $LlamaSource
|
||||
$ResolvedSourceRef = $RequestedLlamaTag
|
||||
$ResolvedSourceRefKind = "tag"
|
||||
$ResolvedLlamaTag = $RequestedLlamaTag
|
||||
|
||||
if ($env:UNSLOTH_LLAMA_FORCE_COMPILE -eq "1") {
|
||||
$NeedLlamaSourceBuild = $true
|
||||
|
|
@ -1693,92 +1722,27 @@ if ($LlamaPr) {
|
|||
$ResolvedSourceRefKind = "pull"
|
||||
$NeedLlamaSourceBuild = $true
|
||||
$SkipPrebuiltInstall = $true
|
||||
} elseif ($SkipPrebuiltInstall) {
|
||||
# Custom source or other override already forced source build; skip the
|
||||
# prebuilt release resolution. When building from a custom fork, the fork
|
||||
# may not carry upstream bNNNN tags.
|
||||
if ($env:UNSLOTH_LLAMA_FORCE_COMPILE -eq "1") {
|
||||
$ResolvedLlamaTag = $RequestedLlamaTag
|
||||
} elseif ($LlamaSource -eq "https://github.com/ggml-org/llama.cpp") {
|
||||
$resolveTagArgs = @("--resolve-llama-tag", $RequestedLlamaTag, "--published-repo", $HelperReleaseRepo, "--output-format", "json")
|
||||
if ($env:UNSLOTH_LLAMA_RELEASE_TAG) { $resolveTagArgs += @("--published-release-tag", $env:UNSLOTH_LLAMA_RELEASE_TAG) }
|
||||
$fallbackResult = Invoke-LlamaHelper -Arguments $resolveTagArgs
|
||||
$fallbackOutput = $fallbackResult.Output
|
||||
$fallbackExit = $fallbackResult.ExitCode
|
||||
$ResolvedLlamaTag = if ($fallbackExit -eq 0 -and $fallbackOutput) {
|
||||
try {
|
||||
(($fallbackOutput | Out-String) | ConvertFrom-Json).llama_tag
|
||||
} catch {
|
||||
$RequestedLlamaTag
|
||||
}
|
||||
} else {
|
||||
$RequestedLlamaTag
|
||||
}
|
||||
} else {
|
||||
$ResolvedLlamaTag = $RequestedLlamaTag
|
||||
}
|
||||
} else {
|
||||
$resolveInstallArgs = @("--resolve-install-tag", $RequestedLlamaTag, "--published-repo", $HelperReleaseRepo, "--output-format", "json")
|
||||
if ($env:UNSLOTH_LLAMA_RELEASE_TAG) { $resolveInstallArgs += @("--published-release-tag", $env:UNSLOTH_LLAMA_RELEASE_TAG) }
|
||||
$resolveErrorLog = New-UnslothTemporaryFile
|
||||
$resolveResult = Invoke-LlamaHelper -Arguments $resolveInstallArgs -StderrPath $resolveErrorLog
|
||||
$resolveOutput = $resolveResult.Output
|
||||
$resolveExit = $resolveResult.ExitCode
|
||||
$ResolvedLlamaTag = if ($resolveOutput) {
|
||||
try {
|
||||
(($resolveOutput | Out-String) | ConvertFrom-Json).llama_tag
|
||||
} catch {
|
||||
""
|
||||
}
|
||||
} else { "" }
|
||||
if ($resolveExit -ne 0 -or [string]::IsNullOrWhiteSpace($ResolvedLlamaTag)) {
|
||||
Write-Host ""
|
||||
substep "Failed to resolve a published llama.cpp release via $HelperReleaseRepo" "Yellow"
|
||||
Write-LlamaFailureLog -Output (Get-Content -Raw $resolveErrorLog)
|
||||
# Resolve the llama.cpp tag for source-build fallback. Pass --published-repo
|
||||
# so the resolver prefers the latest usable Unsloth-published upstream tag
|
||||
# before falling back to the bleeding-edge ggml-org/llama.cpp tag.
|
||||
$resolveFallbackArgs = @("--resolve-llama-tag", $RequestedLlamaTag, "--published-repo", $HelperReleaseRepo, "--output-format", "json")
|
||||
if ($env:UNSLOTH_LLAMA_RELEASE_TAG) { $resolveFallbackArgs += @("--published-release-tag", $env:UNSLOTH_LLAMA_RELEASE_TAG) }
|
||||
$fallbackResult = Invoke-LlamaHelper -Arguments $resolveFallbackArgs
|
||||
$fallbackOutput = $fallbackResult.Output
|
||||
$fallbackExit = $fallbackResult.ExitCode
|
||||
$ResolvedLlamaTag = if ($fallbackExit -eq 0 -and $fallbackOutput) {
|
||||
try {
|
||||
(($fallbackOutput | Out-String) | ConvertFrom-Json).llama_tag
|
||||
} catch {
|
||||
$RequestedLlamaTag
|
||||
}
|
||||
} else {
|
||||
$RequestedLlamaTag
|
||||
}
|
||||
$NeedLlamaSourceBuild = $true
|
||||
$SkipPrebuiltInstall = $true
|
||||
}
|
||||
Remove-Item $resolveErrorLog -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
substep "Resolved llama.cpp release tag: $ResolvedLlamaTag"
|
||||
|
||||
if ($env:UNSLOTH_LLAMA_FORCE_COMPILE -eq "1") {
|
||||
Write-Host ""
|
||||
substep "UNSLOTH_LLAMA_FORCE_COMPILE=1 -- skipping prebuilt llama.cpp install" "Yellow"
|
||||
$NeedLlamaSourceBuild = $true
|
||||
} elseif ($SkipPrebuiltInstall) {
|
||||
Write-Host ""
|
||||
substep "Skipping prebuilt install -- falling back to source build" "Yellow"
|
||||
} else {
|
||||
Write-Host ""
|
||||
substep "installing prebuilt llama.cpp bundle (preferred path)..."
|
||||
if (Test-Path $LlamaCppDir) {
|
||||
substep "Existing llama.cpp install detected -- validating staged prebuilt update before replacement"
|
||||
}
|
||||
if ($SkipPrebuiltInstall) {
|
||||
substep "Skipping prebuilt install because prebuilt tag resolution failed -- falling back to source build" "Yellow"
|
||||
} else {
|
||||
$prebuiltArgs = @(
|
||||
$prebuiltArgs = @(
|
||||
"$PSScriptRoot\install_llama_prebuilt.py",
|
||||
"--install-dir", $LlamaCppDir,
|
||||
"--llama-tag", $RequestedLlamaTag,
|
||||
"--published-repo", $HelperReleaseRepo
|
||||
"--published-repo", $HelperReleaseRepo,
|
||||
"--simple-policy"
|
||||
)
|
||||
if ($env:UNSLOTH_LLAMA_RELEASE_TAG) {
|
||||
$prebuiltArgs += @("--published-release-tag", $env:UNSLOTH_LLAMA_RELEASE_TAG)
|
||||
|
|
@ -1817,6 +1781,10 @@ if ($env:UNSLOTH_LLAMA_FORCE_COMPILE -eq "1") {
|
|||
} else {
|
||||
step "llama.cpp" "prebuilt installed and validated"
|
||||
}
|
||||
$installedRelease = Get-InstalledLlamaPrebuiltRelease -InstallDir $LlamaCppDir
|
||||
if ($installedRelease) {
|
||||
substep $installedRelease
|
||||
}
|
||||
} elseif ($prebuiltExit -eq 3) {
|
||||
step "llama.cpp" "install blocked by active llama.cpp process" "Yellow"
|
||||
Write-LlamaFailureLog -Output $prebuiltOutput
|
||||
|
|
@ -1834,7 +1802,6 @@ if ($env:UNSLOTH_LLAMA_FORCE_COMPILE -eq "1") {
|
|||
substep "Prebuilt llama.cpp path unavailable or failed validation -- falling back to source build" "Yellow"
|
||||
$NeedLlamaSourceBuild = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ==========================================================================
|
||||
|
|
@ -1981,24 +1948,43 @@ if (-not $NeedLlamaSourceBuild) {
|
|||
}
|
||||
|
||||
if (-not $LlamaPr) {
|
||||
if ($LlamaSource -eq "https://github.com/ggml-org/llama.cpp") {
|
||||
$resolveSourceArgs = @("--resolve-source-build", $RequestedLlamaTag, "--published-repo", $HelperReleaseRepo, "--output-format", "json")
|
||||
if ($env:UNSLOTH_LLAMA_RELEASE_TAG) { $resolveSourceArgs += @("--published-release-tag", $env:UNSLOTH_LLAMA_RELEASE_TAG) }
|
||||
$sourcePlanResult = Invoke-LlamaHelper -Arguments $resolveSourceArgs
|
||||
$sourcePlanOutput = $sourcePlanResult.Output
|
||||
$sourcePlanExit = $sourcePlanResult.ExitCode
|
||||
if ($sourcePlanExit -eq 0 -and $sourcePlanOutput) {
|
||||
try {
|
||||
$sourcePlan = ($sourcePlanOutput | Out-String) | ConvertFrom-Json
|
||||
$ResolvedSourceUrl = $sourcePlan.source_url
|
||||
$ResolvedSourceRefKind = $sourcePlan.source_ref_kind
|
||||
$ResolvedSourceRef = $sourcePlan.source_ref
|
||||
} catch {
|
||||
$ResolvedSourceUrl = $LlamaSource
|
||||
if ($env:UNSLOTH_LLAMA_FORCE_COMPILE -eq "1") {
|
||||
if ($RequestedLlamaTag -eq "latest") {
|
||||
$ResolvedSourceRef = if ($env:UNSLOTH_LLAMA_FORCE_COMPILE_REF) {
|
||||
$env:UNSLOTH_LLAMA_FORCE_COMPILE_REF
|
||||
} else {
|
||||
$DefaultLlamaForceCompileRef
|
||||
}
|
||||
$ResolvedSourceRefKind = "branch"
|
||||
} else {
|
||||
$ResolvedSourceRef = $RequestedLlamaTag
|
||||
$ResolvedSourceRefKind = "tag"
|
||||
}
|
||||
} elseif ($RequestedLlamaTag -eq "latest") {
|
||||
$resolveTagArgs = @("--resolve-llama-tag", "latest", "--published-repo", "ggml-org/llama.cpp", "--output-format", "json")
|
||||
$resolveTagResult = Invoke-LlamaHelper -Arguments $resolveTagArgs
|
||||
$resolveTagOutput = $resolveTagResult.Output
|
||||
$resolveTagExit = $resolveTagResult.ExitCode
|
||||
if ($resolveTagExit -eq 0 -and $resolveTagOutput) {
|
||||
try {
|
||||
$ResolvedSourceRef = (($resolveTagOutput | Out-String) | ConvertFrom-Json).llama_tag
|
||||
} catch {
|
||||
$ResolvedSourceRef = ""
|
||||
}
|
||||
} else {
|
||||
$ResolvedSourceRef = ""
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($ResolvedSourceRef)) {
|
||||
$ResolvedSourceRef = "latest"
|
||||
}
|
||||
$ResolvedSourceRefKind = "tag"
|
||||
} else {
|
||||
$ResolvedSourceRef = $RequestedLlamaTag
|
||||
$ResolvedSourceRefKind = "tag"
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($ResolvedSourceUrl)) { $ResolvedSourceUrl = $LlamaSource }
|
||||
if ([string]::IsNullOrWhiteSpace($ResolvedSourceRef)) { $ResolvedSourceRef = $ResolvedLlamaTag }
|
||||
if ([string]::IsNullOrWhiteSpace($ResolvedSourceRef)) { $ResolvedSourceRef = $RequestedLlamaTag }
|
||||
}
|
||||
|
||||
# -- Step A: Clone or pull llama.cpp --
|
||||
|
|
|
|||
272
studio/setup.sh
272
studio/setup.sh
|
|
@ -25,6 +25,7 @@ RULE=$(printf '\342\224\200%.0s' {1..52})
|
|||
_DEFAULT_LLAMA_PR_FORCE=""
|
||||
_DEFAULT_LLAMA_SOURCE="https://github.com/ggml-org/llama.cpp"
|
||||
_DEFAULT_LLAMA_TAG="latest"
|
||||
_DEFAULT_LLAMA_FORCE_COMPILE_REF="master"
|
||||
|
||||
# ── Colors (same palette as startup_banner / install_python_stack) ──
|
||||
if [ -n "${NO_COLOR:-}" ]; then
|
||||
|
|
@ -121,6 +122,45 @@ print_llama_error_log() {
|
|||
tail -n 120 "$log_file" | sed 's/^/ | /' >&2
|
||||
}
|
||||
|
||||
installed_llama_prebuilt_release() {
|
||||
local install_dir=${1:-}
|
||||
local metadata_path="$install_dir/UNSLOTH_PREBUILT_INFO.json"
|
||||
[ -f "$metadata_path" ] || return 0
|
||||
python - "$metadata_path" <<'PY' 2>/dev/null || true
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
raise SystemExit(0)
|
||||
|
||||
if not isinstance(payload, dict):
|
||||
raise SystemExit(0)
|
||||
|
||||
repo = str(payload.get("published_repo") or "").strip()
|
||||
release_tag = str(payload.get("release_tag") or "").strip()
|
||||
llama_tag = str(payload.get("tag") or "").strip()
|
||||
if not repo or not release_tag:
|
||||
raise SystemExit(0)
|
||||
|
||||
message = f"installed release: {repo}@{release_tag}"
|
||||
if llama_tag and llama_tag != release_tag:
|
||||
message += f" (tag {llama_tag})"
|
||||
print(message)
|
||||
PY
|
||||
}
|
||||
|
||||
print_installed_llama_prebuilt_release() {
|
||||
local install_dir=${1:-}
|
||||
local installed_release
|
||||
installed_release="$(installed_llama_prebuilt_release "$install_dir")"
|
||||
if [ -n "$installed_release" ]; then
|
||||
substep "$installed_release"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Banner ──
|
||||
echo ""
|
||||
printf " ${C_TITLE}%s${C_RST}\n" "🦥 Unsloth Studio Setup"
|
||||
|
|
@ -487,30 +527,27 @@ _NEED_LLAMA_SOURCE_BUILD=false
|
|||
_LLAMA_CPP_DEGRADED=false
|
||||
_LLAMA_FORCE_COMPILE="${UNSLOTH_LLAMA_FORCE_COMPILE:-0}"
|
||||
_REQUESTED_LLAMA_TAG="${UNSLOTH_LLAMA_TAG:-${_DEFAULT_LLAMA_TAG}}"
|
||||
# Force all installs to use mainline llama.cpp from ggml-org.
|
||||
_HELPER_RELEASE_REPO="ggml-org/llama.cpp"
|
||||
_HOST_SYSTEM="$(uname -s 2>/dev/null || true)"
|
||||
if [ "$_HOST_SYSTEM" = "Darwin" ]; then
|
||||
_HELPER_RELEASE_REPO="ggml-org/llama.cpp"
|
||||
else
|
||||
_HELPER_RELEASE_REPO="unslothai/llama.cpp"
|
||||
fi
|
||||
_LLAMA_PR="${UNSLOTH_LLAMA_PR:-}"
|
||||
|
||||
_SKIP_PREBUILT_INSTALL=false
|
||||
_LLAMA_PR_FORCE="${UNSLOTH_LLAMA_PR_FORCE:-${_DEFAULT_LLAMA_PR_FORCE}}"
|
||||
# Force mainline source -- no env var override for now.
|
||||
_LLAMA_SOURCE="${_DEFAULT_LLAMA_SOURCE}"
|
||||
_LLAMA_SOURCE="${_LLAMA_SOURCE%.git}" # normalize: strip trailing .git
|
||||
_RESOLVED_SOURCE_URL="$_LLAMA_SOURCE"
|
||||
_RESOLVED_SOURCE_REF="$_REQUESTED_LLAMA_TAG"
|
||||
_RESOLVED_SOURCE_REF_KIND="tag"
|
||||
_RESOLVED_LLAMA_TAG="$_REQUESTED_LLAMA_TAG"
|
||||
|
||||
if [ "$_LLAMA_FORCE_COMPILE" = "1" ]; then
|
||||
_NEED_LLAMA_SOURCE_BUILD=true
|
||||
_SKIP_PREBUILT_INSTALL=true
|
||||
fi
|
||||
|
||||
# Non-default source URL forces source build (fork has different code than prebuilt).
|
||||
if [ "$_LLAMA_SOURCE" != "https://github.com/ggml-org/llama.cpp" ]; then
|
||||
step "llama.cpp" "custom source: $_LLAMA_SOURCE -- forcing source build" "$C_WARN"
|
||||
_NEED_LLAMA_SOURCE_BUILD=true
|
||||
_SKIP_PREBUILT_INSTALL=true
|
||||
fi
|
||||
|
||||
# Baked-in PR_FORCE promotes to _LLAMA_PR when user hasn't set one.
|
||||
if [ -z "$_LLAMA_PR" ] && [ -n "$_LLAMA_PR_FORCE" ] && \
|
||||
[[ "$_LLAMA_PR_FORCE" =~ ^[0-9]+$ ]] && [ "$_LLAMA_PR_FORCE" -gt 0 ]; then
|
||||
|
|
@ -530,149 +567,68 @@ if [ -n "$_LLAMA_PR" ]; then
|
|||
_RESOLVED_SOURCE_REF_KIND="pull"
|
||||
_NEED_LLAMA_SOURCE_BUILD=true
|
||||
_SKIP_PREBUILT_INSTALL=true
|
||||
elif [ "${_SKIP_PREBUILT_INSTALL:-false}" = true ]; then
|
||||
# Custom source or other override already forced source build; skip
|
||||
# the prebuilt release resolution entirely. When building from a custom
|
||||
# fork, the fork may not carry upstream bNNNN tags, so resolve the tag
|
||||
# only when the source is the default ggml-org repo.
|
||||
if [ "$_LLAMA_FORCE_COMPILE" = "1" ]; then
|
||||
_RESOLVED_LLAMA_TAG="$_REQUESTED_LLAMA_TAG"
|
||||
elif [ "$_LLAMA_SOURCE" = "https://github.com/ggml-org/llama.cpp" ]; then
|
||||
_RESOLVE_TAG_ARGS=(--resolve-llama-tag "$_REQUESTED_LLAMA_TAG" --published-repo "$_HELPER_RELEASE_REPO")
|
||||
_RESOLVE_TAG_ARGS+=(--output-format json)
|
||||
if [ -n "${UNSLOTH_LLAMA_RELEASE_TAG:-}" ]; then
|
||||
_RESOLVE_TAG_ARGS+=(--published-release-tag "$UNSLOTH_LLAMA_RELEASE_TAG")
|
||||
fi
|
||||
set +e
|
||||
_RESOLVE_TAG_JSON="$(python "$SCRIPT_DIR/install_llama_prebuilt.py" "${_RESOLVE_TAG_ARGS[@]}" 2>/dev/null)"
|
||||
_RESOLVE_UPSTREAM_STATUS=$?
|
||||
set -e
|
||||
if [ "$_RESOLVE_UPSTREAM_STATUS" -eq 0 ] && [ -n "${_RESOLVE_TAG_JSON:-}" ]; then
|
||||
_RESOLVED_LLAMA_TAG="$(
|
||||
printf '%s' "$_RESOLVE_TAG_JSON" | python -c 'import json,sys; print(json.load(sys.stdin).get("llama_tag",""))' 2>/dev/null || true
|
||||
)"
|
||||
else
|
||||
_RESOLVED_LLAMA_TAG=""
|
||||
fi
|
||||
if [ -z "$_RESOLVED_LLAMA_TAG" ]; then
|
||||
_RESOLVED_LLAMA_TAG="$_REQUESTED_LLAMA_TAG"
|
||||
fi
|
||||
else
|
||||
_RESOLVED_LLAMA_TAG="$_REQUESTED_LLAMA_TAG"
|
||||
fi
|
||||
else
|
||||
_RESOLVE_INSTALL_ARGS=(--resolve-install-tag "$_REQUESTED_LLAMA_TAG" --published-repo "$_HELPER_RELEASE_REPO")
|
||||
_RESOLVE_INSTALL_ARGS+=(--output-format json)
|
||||
if [ -n "${UNSLOTH_LLAMA_RELEASE_TAG:-}" ]; then
|
||||
_RESOLVE_INSTALL_ARGS+=(--published-release-tag "$UNSLOTH_LLAMA_RELEASE_TAG")
|
||||
fi
|
||||
_RESOLVE_LLAMA_LOG="$(mktemp)"
|
||||
set +e
|
||||
_RESOLVE_INSTALL_JSON="$(
|
||||
python "$SCRIPT_DIR/install_llama_prebuilt.py" \
|
||||
"${_RESOLVE_INSTALL_ARGS[@]}" 2>"$_RESOLVE_LLAMA_LOG"
|
||||
)"
|
||||
_RESOLVE_LLAMA_STATUS=$?
|
||||
set -e
|
||||
if [ "$_RESOLVE_LLAMA_STATUS" -eq 0 ]; then
|
||||
_RESOLVED_LLAMA_TAG="$(
|
||||
printf '%s' "${_RESOLVE_INSTALL_JSON:-}" | python -c 'import json,sys; print(json.load(sys.stdin).get("llama_tag",""))' 2>/dev/null || true
|
||||
)"
|
||||
else
|
||||
_RESOLVED_LLAMA_TAG=""
|
||||
fi
|
||||
if [ -z "$_RESOLVED_LLAMA_TAG" ]; then
|
||||
step "llama.cpp" "failed to resolve a published llama.cpp release via $_HELPER_RELEASE_REPO" "$C_WARN"
|
||||
print_llama_error_log "$_RESOLVE_LLAMA_LOG"
|
||||
set +e
|
||||
# Resolve the llama.cpp tag for source-build fallback. Pass --published-repo
|
||||
# so the resolver prefers the latest usable Unsloth-published upstream tag
|
||||
# before falling back to the bleeding-edge ggml-org/llama.cpp tag.
|
||||
_RESOLVE_FALLBACK_ARGS=(--resolve-llama-tag "$_REQUESTED_LLAMA_TAG" --published-repo "$_HELPER_RELEASE_REPO")
|
||||
_RESOLVE_FALLBACK_ARGS+=(--output-format json)
|
||||
if [ -n "${UNSLOTH_LLAMA_RELEASE_TAG:-}" ]; then
|
||||
_RESOLVE_FALLBACK_ARGS+=(--published-release-tag "$UNSLOTH_LLAMA_RELEASE_TAG")
|
||||
fi
|
||||
_RESOLVE_FALLBACK_JSON="$(python "$SCRIPT_DIR/install_llama_prebuilt.py" "${_RESOLVE_FALLBACK_ARGS[@]}" 2>/dev/null)"
|
||||
_RESOLVE_UPSTREAM_STATUS=$?
|
||||
set -e
|
||||
if [ "$_RESOLVE_UPSTREAM_STATUS" -eq 0 ] && [ -n "${_RESOLVE_FALLBACK_JSON:-}" ]; then
|
||||
_RESOLVED_LLAMA_TAG="$(
|
||||
printf '%s' "$_RESOLVE_FALLBACK_JSON" | python -c 'import json,sys; print(json.load(sys.stdin).get("llama_tag",""))' 2>/dev/null || true
|
||||
)"
|
||||
else
|
||||
_RESOLVED_LLAMA_TAG=""
|
||||
fi
|
||||
if [ -z "$_RESOLVED_LLAMA_TAG" ]; then
|
||||
_RESOLVED_LLAMA_TAG="$_REQUESTED_LLAMA_TAG"
|
||||
fi
|
||||
_NEED_LLAMA_SOURCE_BUILD=true
|
||||
_SKIP_PREBUILT_INSTALL=true
|
||||
fi
|
||||
rm -f "$_RESOLVE_LLAMA_LOG"
|
||||
fi
|
||||
|
||||
substep "resolved llama.cpp tag: $_RESOLVED_LLAMA_TAG"
|
||||
verbose_substep "requested llama.cpp tag: $_REQUESTED_LLAMA_TAG (repo: $_HELPER_RELEASE_REPO)"
|
||||
|
||||
if [ "$_LLAMA_FORCE_COMPILE" = "1" ]; then
|
||||
step "llama.cpp" "UNSLOTH_LLAMA_FORCE_COMPILE=1 -- skipping prebuilt" "$C_WARN"
|
||||
_NEED_LLAMA_SOURCE_BUILD=true
|
||||
elif [ "${_SKIP_PREBUILT_INSTALL:-false}" = true ]; then
|
||||
substep "prebuilt install skipped -- falling back to source build"
|
||||
else
|
||||
substep "installing prebuilt llama.cpp..."
|
||||
if [ -d "$LLAMA_CPP_DIR" ]; then
|
||||
substep "existing install detected -- validating update"
|
||||
fi
|
||||
if [ "${_SKIP_PREBUILT_INSTALL:-false}" = true ]; then
|
||||
substep "prebuilt tag resolution failed -- falling back to source build"
|
||||
_PREBUILT_CMD=(
|
||||
python "$SCRIPT_DIR/install_llama_prebuilt.py"
|
||||
--install-dir "$LLAMA_CPP_DIR"
|
||||
--llama-tag "$_REQUESTED_LLAMA_TAG"
|
||||
--published-repo "$_HELPER_RELEASE_REPO"
|
||||
--simple-policy
|
||||
)
|
||||
if [ -n "${UNSLOTH_LLAMA_RELEASE_TAG:-}" ]; then
|
||||
_PREBUILT_CMD+=(--published-release-tag "$UNSLOTH_LLAMA_RELEASE_TAG")
|
||||
fi
|
||||
_PREBUILT_LOG="$(mktemp)"
|
||||
set +e
|
||||
if _is_verbose; then
|
||||
"${_PREBUILT_CMD[@]}" 2>&1 | tee "$_PREBUILT_LOG"
|
||||
_PREBUILT_STATUS=${PIPESTATUS[0]}
|
||||
else
|
||||
_PREBUILT_CMD=(
|
||||
python "$SCRIPT_DIR/install_llama_prebuilt.py"
|
||||
--install-dir "$LLAMA_CPP_DIR"
|
||||
--llama-tag "$_REQUESTED_LLAMA_TAG"
|
||||
--published-repo "$_HELPER_RELEASE_REPO"
|
||||
)
|
||||
if [ -n "${UNSLOTH_LLAMA_RELEASE_TAG:-}" ]; then
|
||||
_PREBUILT_CMD+=(--published-release-tag "$UNSLOTH_LLAMA_RELEASE_TAG")
|
||||
fi
|
||||
_PREBUILT_LOG="$(mktemp)"
|
||||
set +e
|
||||
if _is_verbose; then
|
||||
"${_PREBUILT_CMD[@]}" 2>&1 | tee "$_PREBUILT_LOG"
|
||||
_PREBUILT_STATUS=${PIPESTATUS[0]}
|
||||
else
|
||||
"${_PREBUILT_CMD[@]}" >"$_PREBUILT_LOG" 2>&1
|
||||
_PREBUILT_STATUS=$?
|
||||
fi
|
||||
set -e
|
||||
"${_PREBUILT_CMD[@]}" >"$_PREBUILT_LOG" 2>&1
|
||||
_PREBUILT_STATUS=$?
|
||||
fi
|
||||
set -e
|
||||
|
||||
if [ "$_PREBUILT_STATUS" -eq 0 ]; then
|
||||
if grep -Fq "already matches" "$_PREBUILT_LOG"; then
|
||||
step "llama.cpp" "prebuilt up to date and validated"
|
||||
else
|
||||
step "llama.cpp" "prebuilt installed and validated"
|
||||
fi
|
||||
verbose_substep "llama.cpp install dir: $LLAMA_CPP_DIR"
|
||||
rm -f "$_PREBUILT_LOG"
|
||||
elif [ "$_PREBUILT_STATUS" -eq 3 ]; then
|
||||
step "llama.cpp" "install blocked by active llama.cpp process" "$C_WARN"
|
||||
print_llama_error_log "$_PREBUILT_LOG"
|
||||
rm -f "$_PREBUILT_LOG"
|
||||
if [ -d "$LLAMA_CPP_DIR" ]; then
|
||||
substep "existing install was restored"
|
||||
fi
|
||||
substep "close Studio or other llama.cpp users and retry"
|
||||
exit 3
|
||||
if [ "$_PREBUILT_STATUS" -eq 0 ]; then
|
||||
if grep -Fq "already matches" "$_PREBUILT_LOG"; then
|
||||
step "llama.cpp" "prebuilt up to date and validated"
|
||||
else
|
||||
step "llama.cpp" "prebuilt install failed (continuing)" "$C_WARN"
|
||||
print_llama_error_log "$_PREBUILT_LOG"
|
||||
rm -f "$_PREBUILT_LOG"
|
||||
if [ -d "$LLAMA_CPP_DIR" ]; then
|
||||
substep "prebuilt update failed; existing install restored"
|
||||
fi
|
||||
substep "falling back to source build"
|
||||
_NEED_LLAMA_SOURCE_BUILD=true
|
||||
step "llama.cpp" "prebuilt installed and validated"
|
||||
fi
|
||||
print_installed_llama_prebuilt_release "$LLAMA_CPP_DIR"
|
||||
verbose_substep "llama.cpp install dir: $LLAMA_CPP_DIR"
|
||||
rm -f "$_PREBUILT_LOG"
|
||||
elif [ "$_PREBUILT_STATUS" -eq 3 ]; then
|
||||
step "llama.cpp" "install blocked by active llama.cpp process" "$C_WARN"
|
||||
print_llama_error_log "$_PREBUILT_LOG"
|
||||
rm -f "$_PREBUILT_LOG"
|
||||
if [ -d "$LLAMA_CPP_DIR" ]; then
|
||||
substep "existing install was restored"
|
||||
fi
|
||||
substep "close Studio or other llama.cpp users and retry"
|
||||
exit 3
|
||||
else
|
||||
step "llama.cpp" "prebuilt install failed (continuing)" "$C_WARN"
|
||||
print_llama_error_log "$_PREBUILT_LOG"
|
||||
rm -f "$_PREBUILT_LOG"
|
||||
if [ -d "$LLAMA_CPP_DIR" ]; then
|
||||
substep "prebuilt update failed; existing install restored"
|
||||
fi
|
||||
substep "falling back to source build"
|
||||
_NEED_LLAMA_SOURCE_BUILD=true
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -746,33 +702,41 @@ else
|
|||
[ -f "$LLAMA_SERVER_BIN" ] || _LLAMA_CPP_DEGRADED=true
|
||||
else
|
||||
if [ -z "$_LLAMA_PR" ]; then
|
||||
if [ "$_LLAMA_SOURCE" = "https://github.com/ggml-org/llama.cpp" ]; then
|
||||
_RESOLVE_SOURCE_ARGS=(--resolve-source-build "$_REQUESTED_LLAMA_TAG" --published-repo "$_HELPER_RELEASE_REPO")
|
||||
_RESOLVE_SOURCE_ARGS+=(--output-format json)
|
||||
if [ -n "${UNSLOTH_LLAMA_RELEASE_TAG:-}" ]; then
|
||||
_RESOLVE_SOURCE_ARGS+=(--published-release-tag "$UNSLOTH_LLAMA_RELEASE_TAG")
|
||||
_RESOLVED_SOURCE_URL="$_LLAMA_SOURCE"
|
||||
if [ "$_LLAMA_FORCE_COMPILE" = "1" ]; then
|
||||
if [ "$_REQUESTED_LLAMA_TAG" = "latest" ]; then
|
||||
_RESOLVED_SOURCE_REF="${UNSLOTH_LLAMA_FORCE_COMPILE_REF:-${_DEFAULT_LLAMA_FORCE_COMPILE_REF}}"
|
||||
_RESOLVED_SOURCE_REF_KIND="branch"
|
||||
else
|
||||
_RESOLVED_SOURCE_REF="$_REQUESTED_LLAMA_TAG"
|
||||
_RESOLVED_SOURCE_REF_KIND="tag"
|
||||
fi
|
||||
elif [ "$_REQUESTED_LLAMA_TAG" = "latest" ]; then
|
||||
_RESOLVE_TAG_ARGS=(--resolve-llama-tag latest --published-repo "ggml-org/llama.cpp" --output-format json)
|
||||
set +e
|
||||
_SOURCE_BUILD_PLAN="$(python "$SCRIPT_DIR/install_llama_prebuilt.py" "${_RESOLVE_SOURCE_ARGS[@]}" 2>/dev/null)"
|
||||
_RESOLVE_SOURCE_STATUS=$?
|
||||
_RESOLVE_TAG_JSON="$(python "$SCRIPT_DIR/install_llama_prebuilt.py" "${_RESOLVE_TAG_ARGS[@]}" 2>/dev/null)"
|
||||
_RESOLVE_TAG_STATUS=$?
|
||||
set -e
|
||||
if [ "$_RESOLVE_SOURCE_STATUS" -eq 0 ] && [ -n "$_SOURCE_BUILD_PLAN" ]; then
|
||||
_RESOLVED_SOURCE_URL="$(
|
||||
printf '%s' "$_SOURCE_BUILD_PLAN" | python -c 'import json,sys; print(json.load(sys.stdin).get("source_url",""))' 2>/dev/null || true
|
||||
)"
|
||||
_RESOLVED_SOURCE_REF_KIND="$(
|
||||
printf '%s' "$_SOURCE_BUILD_PLAN" | python -c 'import json,sys; print(json.load(sys.stdin).get("source_ref_kind",""))' 2>/dev/null || true
|
||||
)"
|
||||
if [ "$_RESOLVE_TAG_STATUS" -eq 0 ] && [ -n "${_RESOLVE_TAG_JSON:-}" ]; then
|
||||
_RESOLVED_SOURCE_REF="$(
|
||||
printf '%s' "$_SOURCE_BUILD_PLAN" | python -c 'import json,sys; print(json.load(sys.stdin).get("source_ref",""))' 2>/dev/null || true
|
||||
printf '%s' "$_RESOLVE_TAG_JSON" | python -c 'import json,sys; print(json.load(sys.stdin).get("llama_tag",""))' 2>/dev/null || true
|
||||
)"
|
||||
else
|
||||
_RESOLVED_SOURCE_REF=""
|
||||
fi
|
||||
if [ -z "$_RESOLVED_SOURCE_REF" ]; then
|
||||
_RESOLVED_SOURCE_REF="latest"
|
||||
fi
|
||||
_RESOLVED_SOURCE_REF_KIND="tag"
|
||||
else
|
||||
_RESOLVED_SOURCE_REF="$_REQUESTED_LLAMA_TAG"
|
||||
_RESOLVED_SOURCE_REF_KIND="tag"
|
||||
fi
|
||||
if [ -z "$_RESOLVED_SOURCE_URL" ]; then
|
||||
_RESOLVED_SOURCE_URL="$_LLAMA_SOURCE"
|
||||
fi
|
||||
if [ -z "$_RESOLVED_SOURCE_REF" ]; then
|
||||
_RESOLVED_SOURCE_REF="$_RESOLVED_LLAMA_TAG"
|
||||
_RESOLVED_SOURCE_REF="$_REQUESTED_LLAMA_TAG"
|
||||
fi
|
||||
fi
|
||||
verbose_substep "source build repo: $_RESOLVED_SOURCE_URL"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue