{
return (
-
+
diff --git a/studio/frontend/src/components/assistant-ui/use-intent-aware-autoscroll.tsx b/studio/frontend/src/components/assistant-ui/use-intent-aware-autoscroll.tsx
index d5927f77c1..e4c17a5021 100644
--- a/studio/frontend/src/components/assistant-ui/use-intent-aware-autoscroll.tsx
+++ b/studio/frontend/src/components/assistant-ui/use-intent-aware-autoscroll.tsx
@@ -77,6 +77,14 @@ type AutoScrollContextValue = {
scrollToBottom: ScrollToBottom;
getIsAtBottom: () => boolean;
subscribe: (listener: () => void) => () => void;
+ /**
+ * Mark the user as detached from the bottom, as if they had scrolled
+ * up. Called when the composer grows and the bottom spacer grows with
+ * it: the chat is then above the new bottom, and observer-driven pins
+ * must not shove it up. Scrolling back to the bottom re-attaches;
+ * explicit pins (run start, scroll-to-bottom button) still work.
+ */
+ detachFromBottom: () => void;
};
const noopContext: AutoScrollContextValue = {
@@ -87,6 +95,9 @@ const noopContext: AutoScrollContextValue = {
subscribe: () => () => {
/* no-op */
},
+ detachFromBottom: () => {
+ /* no viewport mounted */
+ },
};
const AutoScrollContext = createContext
(noopContext);
@@ -129,6 +140,9 @@ export function useIntentAwareAutoScroll(): {
const scrollImplRef = useRef(() => {
/* no viewport mounted */
});
+ const detachImplRef = useRef<() => void>(() => {
+ /* no viewport mounted */
+ });
const getIsAtBottom = useCallback(() => isAtBottomRef.current, []);
@@ -153,8 +167,12 @@ export function useIntentAwareAutoScroll(): {
scrollImplRef.current(behavior);
}, []);
+ const detachFromBottom = useCallback(() => {
+ detachImplRef.current();
+ }, []);
+
const attach = useCallback(
- (el: HTMLElement) => {
+ (el: HTMLElement, isRebind: boolean) => {
let rafId: number | null = null;
let lastScrollTop = el.scrollTop;
let lastClientWidth = el.clientWidth;
@@ -286,6 +304,13 @@ export function useIntentAwareAutoScroll(): {
requestTick();
};
+ // Programmatic detach (see detachFromBottom). Same effect as the
+ // user scrolling up; the tick refresh updates isAtBottom.
+ detachImplRef.current = () => {
+ detach();
+ requestTick();
+ };
+
const onWheel = (e: WheelEvent) => {
if (
e.deltaY < 0 &&
@@ -476,21 +501,24 @@ export function useIntentAwareAutoScroll(): {
const mutationObserver = new MutationObserver(onLayoutChange);
const onViewportResize = onLayoutChange;
- // Fresh attach always starts pinned. `userDetachedRef` survives
- // ref rebinds (it's hook-scoped), so if the viewport element is
- // ever unmounted and remounted without an AUI lifecycle event
- // (e.g. a parent layout refactor that remounts the viewport),
- // a prior detach would silently disable auto-follow for the
- // rest of the session.
- userDetachedRef.current = false;
+ // Fresh attach (a new viewport element) always starts pinned.
+ // Rebinds to the SAME element must not pin or reset detach state:
+ // the Viewport composes refs with an identity that changes on
+ // re-render, so React re-runs the ref (null, then same element)
+ // on unrelated renders such as composer resizes. Pinning here
+ // would yank the chat to the bottom on every such render. The
+ // observers below are re-installed either way.
+ if (!isRebind) {
+ userDetachedRef.current = false;
- // Pin to bottom when the ref first attaches. Covers the case
- // where `thread.initialize` fires before the ref is bound.
- extendFollow();
- if (el.scrollHeight > el.clientHeight) {
- el.scrollTo({ top: el.scrollHeight, behavior: "instant" });
+ // Pin to bottom when the ref first attaches. Covers the case
+ // where `thread.initialize` fires before the ref is bound.
+ extendFollow();
+ if (el.scrollHeight > el.clientHeight) {
+ el.scrollTo({ top: el.scrollHeight, behavior: "instant" });
+ }
+ setIsAtBottom(true);
}
- setIsAtBottom(true);
requestTick();
// Observe the border box, not the content box. The stabilizer
@@ -544,6 +572,9 @@ export function useIntentAwareAutoScroll(): {
scrollImplRef.current = () => {
/* no viewport mounted */
};
+ detachImplRef.current = () => {
+ /* no viewport mounted */
+ };
};
},
[setIsAtBottom],
@@ -562,6 +593,7 @@ export function useIntentAwareAutoScroll(): {
useAuiEvent("thread.initialize", () => pinToBottom("instant"));
useAuiEvent("threadListItem.switchedTo", () => pinToBottom("instant"));
+ const lastElRef = useRef(null);
const ref = useCallback>(
(el) => {
if (cleanupRef.current) {
@@ -569,15 +601,20 @@ export function useIntentAwareAutoScroll(): {
cleanupRef.current = null;
}
if (el) {
- cleanupRef.current = attach(el);
+ // Same-element rebind vs a genuinely new element, see attach().
+ const isRebind = lastElRef.current === el;
+ lastElRef.current = el;
+ cleanupRef.current = attach(el, isRebind);
}
+ // On null, keep lastElRef so a rebind to the same element is
+ // recognized; a real remount binds a different element anyway.
},
[attach],
);
const context = useMemo(
- () => ({ scrollToBottom, getIsAtBottom, subscribe }),
- [scrollToBottom, getIsAtBottom, subscribe],
+ () => ({ scrollToBottom, getIsAtBottom, subscribe, detachFromBottom }),
+ [scrollToBottom, getIsAtBottom, subscribe, detachFromBottom],
);
return { ref, context };
diff --git a/studio/frontend/src/features/chat/hooks/use-chat-model-runtime.ts b/studio/frontend/src/features/chat/hooks/use-chat-model-runtime.ts
index e6892b34fd..d1dab924cb 100644
--- a/studio/frontend/src/features/chat/hooks/use-chat-model-runtime.ts
+++ b/studio/frontend/src/features/chat/hooks/use-chat-model-runtime.ts
@@ -94,16 +94,25 @@ function describeModel(model: {
is_lora?: boolean;
is_vision?: boolean;
is_gguf?: boolean;
+ is_mlx?: boolean;
is_audio?: boolean;
has_audio_input?: boolean;
}): string | undefined {
const tags: string[] = [];
if (model.is_gguf) tags.push("GGUF");
+ if (model.is_mlx) tags.push("MLX");
if (model.is_lora) tags.push("LoRA");
if (model.is_vision) tags.push("Vision");
if (model.is_audio) tags.push("Audio");
if (model.has_audio_input) tags.push("Audio Input");
- if (!model.is_lora && !model.is_vision && !model.is_gguf && !model.is_audio && !model.has_audio_input)
+ if (
+ !model.is_lora &&
+ !model.is_vision &&
+ !model.is_gguf &&
+ !model.is_mlx &&
+ !model.is_audio &&
+ !model.has_audio_input
+ )
tags.push("Base");
return tags.join(" · ");
}
@@ -114,6 +123,7 @@ function toChatModelSummary(model: {
is_lora?: boolean;
is_vision?: boolean;
is_gguf?: boolean;
+ is_mlx?: boolean;
is_audio?: boolean;
audio_type?: string | null;
has_audio_input?: boolean;
@@ -125,6 +135,7 @@ function toChatModelSummary(model: {
isLora: Boolean(model.is_lora),
isVision: Boolean(model.is_vision),
isGguf: Boolean(model.is_gguf),
+ isMlx: Boolean(model.is_mlx),
isAudio: Boolean(model.is_audio),
audioType: model.audio_type ?? null,
hasAudioInput: Boolean(model.has_audio_input),
diff --git a/studio/frontend/src/features/chat/types/api.ts b/studio/frontend/src/features/chat/types/api.ts
index d313b43438..92ea1500bd 100644
--- a/studio/frontend/src/features/chat/types/api.ts
+++ b/studio/frontend/src/features/chat/types/api.ts
@@ -7,6 +7,7 @@ export interface BackendModelDetails {
is_vision?: boolean;
is_lora?: boolean;
is_gguf?: boolean;
+ is_mlx?: boolean;
is_audio?: boolean;
audio_type?: string | null;
has_audio_input?: boolean;
diff --git a/studio/frontend/src/features/chat/types/runtime.ts b/studio/frontend/src/features/chat/types/runtime.ts
index 4c44ee1e9c..24286c94fb 100644
--- a/studio/frontend/src/features/chat/types/runtime.ts
+++ b/studio/frontend/src/features/chat/types/runtime.ts
@@ -44,6 +44,7 @@ export interface ChatModelSummary {
isVision: boolean;
isLora: boolean;
isGguf?: boolean;
+ isMlx?: boolean;
isAudio?: boolean;
audioType?: string | null;
hasAudioInput?: boolean;
diff --git a/studio/install_llama_prebuilt.py b/studio/install_llama_prebuilt.py
index 62b7b2b290..09953843f0 100644
--- a/studio/install_llama_prebuilt.py
+++ b/studio/install_llama_prebuilt.py
@@ -70,7 +70,12 @@ def windows_hidden_subprocess_kwargs() -> dict[str, object]:
return kwargs
-def env_int(name: str, default: int, *, minimum: int | None = None) -> int:
+def env_int(
+ name: str,
+ default: int,
+ *,
+ minimum: int | None = None,
+) -> int:
raw = os.environ.get(name)
if raw is None:
value = default
@@ -104,9 +109,7 @@ UPSTREAM_REPO = "ggml-org/llama.cpp"
UPSTREAM_RELEASES_API = f"https://api.github.com/repos/{UPSTREAM_REPO}/releases/latest"
LEMONADE_ROCM_REPO = "lemonade-sdk/llamacpp-rocm"
-LEMONADE_ROCM_RELEASES_API = (
- f"https://api.github.com/repos/{LEMONADE_ROCM_REPO}/releases/latest"
-)
+LEMONADE_ROCM_RELEASES_API = f"https://api.github.com/repos/{LEMONADE_ROCM_REPO}/releases/latest"
def _lemonade_release_api_for(llama_tag: str) -> str:
@@ -135,9 +138,7 @@ def _lemonade_release_api_for(llama_tag: str) -> str:
)
-TEST_MODEL_URL = (
- "https://huggingface.co/ggml-org/models/resolve/main/tinyllamas/stories260K.gguf"
-)
+TEST_MODEL_URL = "https://huggingface.co/ggml-org/models/resolve/main/tinyllamas/stories260K.gguf"
TEST_MODEL_SHA256 = "270cba1bd5109f42d03350f60406024560464db173c0e387d91f0426d3bd256d"
VALIDATION_MODEL_CACHE_DIRNAME = ".cache"
VALIDATION_MODEL_CACHE_FILENAME = "stories260K.gguf"
@@ -256,12 +257,8 @@ _BLACKWELL_MIN_SM = 120
# windows-cuda build at or above this already covers Blackwell and makes the
# older pinned 13.1 fallback unnecessary (cuda-12.4 is below it).
_BLACKWELL_MIN_TOOLKIT = (12, 8)
-_PINNED_BLACKWELL_LLAMA_SHA256 = (
- "31ddb8b42d7ab4a47cab8c48c397519f580ca502df7e73f3ab396eacc16c8e8d"
-)
-_PINNED_BLACKWELL_CUDART_SHA256 = (
- "f96935e7e385e3b2d0189239077c10fe8fd7e95690fea4afec455b1b6c7e3f18"
-)
+_PINNED_BLACKWELL_LLAMA_SHA256 = "31ddb8b42d7ab4a47cab8c48c397519f580ca502df7e73f3ab396eacc16c8e8d"
+_PINNED_BLACKWELL_CUDART_SHA256 = "f96935e7e385e3b2d0189239077c10fe8fd7e95690fea4afec455b1b6c7e3f18"
def _cuda_runtime_lines_for_major(major: int) -> list[str]:
@@ -279,9 +276,7 @@ def _resolve_linux_bundle_profile(bundle_profile: str) -> "dict[str, Any] | None
known = DIRECT_LINUX_BUNDLE_PROFILES.get(bundle_profile)
if known is not None:
return known
- m = re.fullmatch(
- r"cuda(?P\d+)-(?Polder|newer|portable)", bundle_profile
- )
+ m = re.fullmatch(r"cuda(?P\d+)-(?Polder|newer|portable)", bundle_profile)
if not m:
return None
base_key = max(
@@ -788,9 +783,9 @@ def refs_match(candidate_ref: str | None, requested_ref: str | None) -> bool:
candidate_commit = normalize_source_commit(candidate_ref)
requested_commit = normalize_source_commit(requested_ref)
if candidate_commit and requested_commit:
- return candidate_commit.startswith(
- requested_commit
- ) or requested_commit.startswith(candidate_commit)
+ return candidate_commit.startswith(requested_commit) or requested_commit.startswith(
+ candidate_commit
+ )
return False
@@ -820,9 +815,7 @@ def windows_cuda_upstream_asset_names(llama_tag: str, runtime: str) -> list[str]
def windows_cuda_asset_aliases(
- asset_name: str,
- *,
- compatibility_tag: str | None = None,
+ asset_name: str, *, compatibility_tag: str | None = None
) -> list[str]:
aliases: list[str] = []
legacy_match = re.fullmatch(
@@ -886,11 +879,7 @@ class DownloadProgress:
self.last_emit = 0.0
term_ok = os.environ.get("TERM", "").lower() != "dumb"
self.stream = (
- sys.stderr
- if sys.stderr.isatty()
- else sys.stdout
- if sys.stdout.isatty()
- else sys.stderr
+ sys.stderr if sys.stderr.isatty() else sys.stdout if sys.stdout.isatty() else sys.stderr
)
self.is_tty = term_ok and self.stream.isatty()
self.completed = False
@@ -898,7 +887,12 @@ class DownloadProgress:
self.last_milestone_bytes = 0
self.has_rendered_tty_progress = False
- def _render(self, downloaded_bytes: int, *, final: bool = False) -> str:
+ def _render(
+ self,
+ downloaded_bytes: int,
+ *,
+ final: bool = False,
+ ) -> str:
elapsed = max(time.monotonic() - self.start_time, 1e-6)
speed = downloaded_bytes / elapsed
speed_text = f"{format_byte_count(speed)}/s"
@@ -918,10 +912,7 @@ class DownloadProgress:
if self.is_tty:
elapsed = now - self.start_time
if not self.has_rendered_tty_progress:
- if (
- self.total_bytes is not None
- and downloaded_bytes >= self.total_bytes
- ):
+ if self.total_bytes is not None and downloaded_bytes >= self.total_bytes:
return
if elapsed < TTY_PROGRESS_START_DELAY_SECONDS:
return
@@ -943,10 +934,7 @@ class DownloadProgress:
if self.total_bytes is not None:
percent = int((downloaded_bytes * 100) / max(self.total_bytes, 1))
milestone_percent = min((percent // 25) * 25, 100)
- if (
- milestone_percent > self.last_milestone_percent
- and milestone_percent < 100
- ):
+ if milestone_percent > self.last_milestone_percent and milestone_percent < 100:
self.last_milestone_percent = milestone_percent
should_emit = True
else:
@@ -999,11 +987,7 @@ def download_bytes(
content_length = response.headers.get("Content-Length")
if content_length and content_length.isdigit():
total_bytes = int(content_length)
- progress = (
- DownloadProgress(progress_label, total_bytes)
- if progress_label
- else None
- )
+ progress = DownloadProgress(progress_label, total_bytes) if progress_label else None
data = bytearray()
while True:
chunk = response.read(1024 * 1024)
@@ -1033,17 +1017,13 @@ def fetch_json(url: str) -> Any:
data = download_bytes(
url,
timeout = 30,
- headers = github_api_headers(url)
- if is_github_api_url(url)
- else auth_headers(url),
+ 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"
- )
+ 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:
@@ -1052,9 +1032,7 @@ def fetch_json(url: str) -> Any:
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}"
- )
+ last_decode_exc = RuntimeError(f"downloaded invalid JSON from {url}: {exc}")
else:
if not isinstance(payload, dict) and not isinstance(payload, list):
raise RuntimeError(
@@ -1088,9 +1066,7 @@ def download_file(url: str, destination: Path) -> None:
content_length = response.headers.get("Content-Length")
if content_length and content_length.isdigit():
total_bytes = int(content_length)
- progress = DownloadProgress(
- f"Downloading {destination.name}", total_bytes
- )
+ progress = DownloadProgress(f"Downloading {destination.name}", total_bytes)
downloaded_bytes = 0
while True:
chunk = response.read(1024 * 1024)
@@ -1115,27 +1091,19 @@ def download_file(url: str, destination: Path) -> None:
pass
if attempt >= HTTP_FETCH_ATTEMPTS or not is_retryable_url_error(exc):
raise
- log(
- f"download failed ({attempt}/{HTTP_FETCH_ATTEMPTS}) for {url}: {exc}; retrying"
- )
+ log(f"download failed ({attempt}/{HTTP_FETCH_ATTEMPTS}) for {url}: {exc}; retrying")
sleep_backoff(attempt, exc = exc)
assert last_exc is not None
raise last_exc
def download_file_verified(
- url: str,
- destination: Path,
- *,
- expected_sha256: str | None,
- label: str,
+ url: str, destination: Path, *, expected_sha256: str | None, label: str
) -> None:
normalized_expected = normalize_sha256_digest(expected_sha256)
if not normalized_expected:
download_file(url, destination)
- log(
- f"downloaded {label} without a published sha256; relying on install validation"
- )
+ log(f"downloaded {label} without a published sha256; relying on install validation")
return
for attempt in range(1, 3):
@@ -1219,9 +1187,7 @@ def latest_upstream_release_tag() -> str:
payload = fetch_json(UPSTREAM_RELEASES_API)
tag = payload.get("tag_name")
if not isinstance(tag, str) or not tag:
- raise RuntimeError(
- f"latest release tag was missing from {UPSTREAM_RELEASES_API}"
- )
+ raise RuntimeError(f"latest release tag was missing from {UPSTREAM_RELEASES_API}")
return tag
@@ -1256,19 +1222,13 @@ def iter_release_payloads_by_time(
yield github_release(repo, published_release_tag)
return
- if (
- requested_tag
- and requested_tag != "latest"
- and is_release_tag_like(requested_tag)
- ):
+ 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"
- )
+ log(f"release tag {requested_tag} not found in {repo}; scanning recent releases")
else:
raise
except Exception:
@@ -1276,21 +1236,15 @@ def iter_release_payloads_by_time(
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")
+ 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:
+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):
@@ -1392,10 +1346,7 @@ def parse_direct_linux_release_bundle(
def direct_linux_release_plan(
- release: dict[str, Any],
- host: HostInfo,
- repo: str,
- requested_tag: str,
+ 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:
@@ -1477,10 +1428,7 @@ def direct_linux_release_plan(
def direct_upstream_release_plan(
- release: dict[str, Any],
- host: HostInfo,
- repo: str,
- requested_tag: str,
+ 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:
@@ -1674,9 +1622,7 @@ def resolve_simple_install_release_plans(
f"{repo} ships only linux-x64 prebuilts; "
f"{host.machine or 'non-x64'} Linux falls back to source build"
)
- allow_older_release_fallback = (
- requested_tag == "latest" and not published_release_tag
- )
+ allow_older_release_fallback = requested_tag == "latest" and not published_release_tag
# macOS: pin the last upstream build that loads on a pre-26 host instead of
# fetching the latest (macOS 26 only) build and walking back release by
# release. No-op on macOS 26+, unknown version, non-macOS, and the fork.
@@ -1690,17 +1636,13 @@ def resolve_simple_install_release_plans(
last_error: PrebuiltFallback | None = None
try:
- releases = iter_release_payloads_by_time(
- repo, published_release_tag, requested_tag
- )
+ 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
- )
+ plan = direct_upstream_release_plan(release, host, repo, requested_tag)
if plan is None:
continue
except PrebuiltFallback as exc:
@@ -1720,17 +1662,13 @@ def resolve_simple_install_release_plans(
except PrebuiltFallback:
raise
except Exception as exc:
- raise PrebuiltFallback(
- f"failed to inspect published releases in {repo}: {exc}"
- ) from 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}"
- )
+ raise PrebuiltFallback(f"no installable published llama.cpp releases were found in {repo}")
def normalized_requested_llama_tag(requested_tag: str | None) -> str:
@@ -1782,9 +1720,7 @@ def parse_cuda_visible_devices(value: str | None) -> list[str] | None:
return [token.strip() for token in raw.split(",") if token.strip()]
-def supports_explicit_visible_device_matching(
- visible_devices: list[str] | None,
-) -> bool:
+def supports_explicit_visible_device_matching(visible_devices: list[str] | None) -> bool:
if not visible_devices:
return False
for token in visible_devices:
@@ -1796,8 +1732,7 @@ def supports_explicit_visible_device_matching(
def select_visible_gpu_rows(
- gpu_rows: Iterable[tuple[str, str, str]],
- visible_devices: list[str] | None,
+ gpu_rows: Iterable[tuple[str, str, str]], visible_devices: list[str] | None
) -> list[tuple[str, str, str]]:
rows = list(gpu_rows)
if visible_devices is None:
@@ -1835,9 +1770,7 @@ def dir_provides_exact_library(directory: str | Path, library: str) -> bool:
return candidate.exists() and (candidate.is_file() or candidate.is_symlink())
-def linux_runtime_dirs_for_required_libraries(
- required_libraries: Iterable[str],
-) -> list[str]:
+def linux_runtime_dirs_for_required_libraries(required_libraries: Iterable[str]) -> list[str]:
required = [library for library in required_libraries if library]
candidates: list[str | Path] = []
@@ -1853,9 +1786,7 @@ def linux_runtime_dirs_for_required_libraries(
value = os.environ.get(name)
if value:
cuda_roots.append(Path(value))
- cuda_roots.extend(
- Path(path) for path in glob_paths("/usr/local/cuda", "/usr/local/cuda-*")
- )
+ cuda_roots.extend(Path(path) for path in glob_paths("/usr/local/cuda", "/usr/local/cuda-*"))
for root in cuda_roots:
candidates.extend(
@@ -1880,8 +1811,7 @@ def linux_runtime_dirs_for_required_libraries(
)
)
candidates.extend(
- Path(path)
- for path in glob_paths("/usr/local/lib/ollama/cuda_v*", "/usr/lib/wsl/lib")
+ Path(path) for path in glob_paths("/usr/local/lib/ollama/cuda_v*", "/usr/lib/wsl/lib")
)
candidates.extend(Path(path) for path in python_runtime_dirs())
candidates.extend(Path(path) for path in ldconfig_runtime_dirs(required))
@@ -1893,9 +1823,7 @@ def linux_runtime_dirs_for_required_libraries(
matched: list[tuple[int, str]] = []
for directory in resolved:
base = Path(directory)
- provided = sum(
- 1 for library in required if dir_provides_exact_library(directory, library)
- )
+ provided = sum(1 for library in required if dir_provides_exact_library(directory, library))
if provided:
matched.append((provided, directory))
@@ -1916,9 +1844,7 @@ def detected_linux_runtime_lines() -> tuple[list[str], dict[str, list[str]]]:
matching_dirs: list[str] = []
for library in required:
matched_dirs = [
- directory
- for directory in dirs
- if any(Path(directory).glob(f"{library}*"))
+ directory for directory in dirs if any(Path(directory).glob(f"{library}*"))
]
if not matched_dirs:
library_matches = {}
@@ -1955,17 +1881,13 @@ def parse_published_artifact(raw: Any) -> PublishedLlamaArtifact | None:
if not isinstance(asset_name, str) or not asset_name:
raise ValueError("artifact.asset_name was missing or not a string")
if not isinstance(install_kind, str) or not install_kind:
- raise ValueError(
- f"artifact {asset_name} install_kind was missing or not a string"
- )
+ raise ValueError(f"artifact {asset_name} install_kind was missing or not a string")
supported_sms_raw = raw.get("supported_sms", [])
if not isinstance(supported_sms_raw, (list, tuple)):
raise ValueError(f"artifact {asset_name} supported_sms must be a list or tuple")
if any(not isinstance(value, (int, str)) for value in supported_sms_raw):
- raise ValueError(
- f"artifact {asset_name} supported_sms entries must be ints or strings"
- )
+ raise ValueError(f"artifact {asset_name} supported_sms entries must be ints or strings")
supported_sms = normalize_compute_caps(supported_sms_raw)
min_sm_raw = raw.get("min_sm")
@@ -1974,9 +1896,7 @@ def parse_published_artifact(raw: Any) -> PublishedLlamaArtifact | None:
min_sm = int(min_sm_raw) if min_sm_raw is not None else None
max_sm = int(max_sm_raw) if max_sm_raw is not None else None
except (TypeError, ValueError) as exc:
- raise ValueError(
- f"artifact {asset_name} min_sm/max_sm were not integers"
- ) from exc
+ raise ValueError(f"artifact {asset_name} min_sm/max_sm were not integers") from exc
runtime_line = raw.get("runtime_line")
coverage_class = raw.get("coverage_class")
bundle_profile = raw.get("bundle_profile")
@@ -1994,9 +1914,7 @@ def parse_published_artifact(raw: Any) -> PublishedLlamaArtifact | None:
return PublishedLlamaArtifact(
asset_name = asset_name,
install_kind = install_kind,
- runtime_line = runtime_line
- if isinstance(runtime_line, str) and runtime_line
- else None,
+ runtime_line = runtime_line if isinstance(runtime_line, str) and runtime_line else None,
coverage_class = coverage_class
if isinstance(coverage_class, str) and coverage_class
else None,
@@ -2071,9 +1989,7 @@ def parse_published_release_bundle(
try:
artifact = parse_published_artifact(raw_artifact)
except ValueError as exc:
- log(
- f"published artifact ignored for {repo}@{release_tag} artifact[{index}]: {exc}"
- )
+ log(f"published artifact ignored for {repo}@{release_tag} artifact[{index}]: {exc}")
continue
if artifact is not None:
artifacts.append(artifact)
@@ -2092,9 +2008,7 @@ def parse_published_release_bundle(
release_tag = release_tag,
upstream_tag = upstream_tag,
manifest_sha256 = manifest_sha256,
- source_repo = source_repo
- if isinstance(source_repo, str) and source_repo
- else None,
+ source_repo = source_repo if isinstance(source_repo, str) and source_repo else None,
source_repo_url = source_repo_url
if isinstance(source_repo_url, str) and source_repo_url
else None,
@@ -2117,9 +2031,7 @@ def parse_published_release_bundle(
def parse_approved_release_checksums(
- repo: str,
- release_tag: str,
- payload: Any,
+ repo: str, release_tag: str, payload: Any
) -> ApprovedReleaseChecksums:
if not isinstance(payload, dict):
raise RuntimeError(
@@ -2157,18 +2069,12 @@ def parse_approved_release_checksums(
artifacts: dict[str, ApprovedArtifactHash] = {}
for asset_name, raw_entry in artifacts_payload.items():
if not isinstance(asset_name, str) or not asset_name:
- raise RuntimeError(
- "published checksum asset used a non-string artifact key"
- )
+ raise RuntimeError("published checksum asset used a non-string artifact key")
if not isinstance(raw_entry, dict):
- raise RuntimeError(
- f"published checksum entry for {asset_name} was not an object"
- )
+ raise RuntimeError(f"published checksum entry for {asset_name} was not an object")
digest = normalize_sha256_digest(raw_entry.get("sha256"))
if not digest:
- raise RuntimeError(
- f"published checksum entry for {asset_name} omitted a valid sha256"
- )
+ raise RuntimeError(f"published checksum entry for {asset_name} omitted a valid sha256")
repo_value = raw_entry.get("repo")
kind_value = raw_entry.get("kind")
artifacts[asset_name] = ApprovedArtifactHash(
@@ -2189,9 +2095,7 @@ def parse_approved_release_checksums(
repo = repo,
release_tag = release_tag,
upstream_tag = upstream_tag,
- source_repo = source_repo
- if isinstance(source_repo, str) and source_repo
- else None,
+ source_repo = source_repo if isinstance(source_repo, str) and source_repo else None,
source_repo_url = source_repo_url
if isinstance(source_repo_url, str) and source_repo_url
else None,
@@ -2210,9 +2114,7 @@ def parse_approved_release_checksums(
)
-def load_approved_release_checksums(
- repo: str, release_tag: str
-) -> ApprovedReleaseChecksums:
+def load_approved_release_checksums(repo: str, release_tag: str) -> ApprovedReleaseChecksums:
try:
release = github_release(repo, release_tag)
except Exception as exc:
@@ -2246,9 +2148,7 @@ def iter_published_release_bundles(
else github_releases(repo, max_pages = DEFAULT_GITHUB_RELEASE_SCAN_MAX_PAGES)
)
for release in releases:
- if not published_release_tag and (
- release.get("draft") or release.get("prerelease")
- ):
+ if not published_release_tag and (release.get("draft") or release.get("prerelease")):
continue
try:
bundle = parse_published_release_bundle(repo, release)
@@ -2301,13 +2201,9 @@ def linux_cuda_choice_from_release(
)
)
published_artifacts = [
- artifact
- for artifact in release.artifacts
- if artifact.install_kind == "linux-cuda"
+ artifact for artifact in release.artifacts if artifact.install_kind == "linux-cuda"
]
- published_asset_names = sorted(
- artifact.asset_name for artifact in published_artifacts
- )
+ published_asset_names = sorted(artifact.asset_name for artifact in published_artifacts)
selection_log.append(
"linux_cuda_selection: published_assets="
+ (",".join(published_asset_names) if published_asset_names else "none")
@@ -2343,9 +2239,7 @@ def linux_cuda_choice_from_release(
attempts: list[AssetChoice] = []
seen_attempts: set[str] = set()
- def add_attempt(
- artifact: PublishedLlamaArtifact, asset_url: str, reason: str
- ) -> None:
+ def add_attempt(artifact: PublishedLlamaArtifact, asset_url: str, reason: str) -> None:
asset_name = artifact.asset_name
if asset_name in seen_attempts:
return
@@ -2382,9 +2276,7 @@ def linux_cuda_choice_from_release(
asset_name = artifact.asset_name
asset_url = release.assets.get(asset_name)
if not asset_url:
- selection_log.append(
- f"linux_cuda_selection: reject {asset_name} missing asset"
- )
+ selection_log.append(f"linux_cuda_selection: reject {asset_name} missing asset")
continue
if not host_sms and artifact.coverage_class != "portable":
selection_log.append(
@@ -2412,9 +2304,7 @@ def linux_cuda_choice_from_release(
supported_sms = {str(value) for value in artifact.supported_sms}
missing_sms = [sm for sm in host_sms if sm not in supported_sms]
out_of_range_sms = [
- sm
- for sm in host_sms
- if not (artifact.min_sm <= int(sm) <= artifact.max_sm)
+ sm for sm in host_sms if not (artifact.min_sm <= int(sm) <= artifact.max_sm)
]
reasons: list[str] = []
if missing_sms:
@@ -2458,8 +2348,7 @@ def linux_cuda_choice_from_release(
return None
selection_log.append(
- "linux_cuda_selection: attempt_order="
- + ",".join(choice.name for choice in attempts)
+ "linux_cuda_selection: attempt_order=" + ",".join(choice.name for choice in attempts)
)
for attempt in attempts:
attempt.selection_log = list(selection_log) + [
@@ -2477,9 +2366,7 @@ 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, max_pages = DEFAULT_GITHUB_RELEASE_SCAN_MAX_PAGES
- ):
+ 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
@@ -2514,9 +2401,7 @@ def validated_checksums_for_bundle(
return checksums
-def published_release_matches_request(
- bundle: PublishedReleaseBundle, requested_ref: str
-) -> bool:
+def published_release_matches_request(bundle: PublishedReleaseBundle, requested_ref: str) -> bool:
if requested_ref == "latest":
return True
for candidate in (
@@ -2571,9 +2456,7 @@ def resolve_published_release(
raise PrebuiltFallback(
f"no usable published llama.cpp releases were available in {repo}"
)
- raise PrebuiltFallback(
- f"no published llama.cpp releases were available in {repo}"
- )
+ raise PrebuiltFallback(f"no published llama.cpp releases were available in {repo}")
raise PrebuiltFallback(
f"no published prebuilt release in {repo} matched upstream tag {normalized_requested}"
@@ -2632,9 +2515,7 @@ def iter_resolved_published_releases(
return
if normalized_requested == "latest":
- raise PrebuiltFallback(
- f"no published llama.cpp releases were available in {repo}"
- )
+ raise PrebuiltFallback(f"no published llama.cpp releases were available in {repo}")
raise PrebuiltFallback(
f"no published prebuilt release in {repo} matched upstream tag {normalized_requested}"
@@ -2692,33 +2573,23 @@ def resolve_requested_install_tag(
).bundle.upstream_tag
-def exact_source_archive_hash(
- checksums: ApprovedReleaseChecksums,
-) -> ApprovedArtifactHash | None:
+def exact_source_archive_hash(checksums: ApprovedReleaseChecksums) -> ApprovedArtifactHash | None:
if not checksums.source_commit:
return None
- return checksums.artifacts.get(
- exact_source_archive_logical_name(checksums.source_commit)
- )
+ return checksums.artifacts.get(exact_source_archive_logical_name(checksums.source_commit))
def source_clone_url_from_checksums(checksums: ApprovedReleaseChecksums) -> str | None:
return source_repo_clone_url(checksums.source_repo, checksums.source_repo_url)
-def source_build_plan_for_release(
- release: ResolvedPublishedRelease,
-) -> SourceBuildPlan:
+def source_build_plan_for_release(release: ResolvedPublishedRelease) -> SourceBuildPlan:
checksums = release.checksums
exact_source = exact_source_archive_hash(checksums)
source_repo = checksums.source_repo or release.bundle.source_repo
source_repo_url = checksums.source_repo_url or release.bundle.source_repo_url
- requested_source_ref = (
- checksums.requested_source_ref or release.bundle.requested_source_ref
- )
- resolved_source_ref = (
- checksums.resolved_source_ref or release.bundle.resolved_source_ref
- )
+ requested_source_ref = checksums.requested_source_ref or release.bundle.requested_source_ref
+ resolved_source_ref = checksums.resolved_source_ref or release.bundle.resolved_source_ref
source_commit = checksums.source_commit or release.bundle.source_commit
source_ref_kind = checksums.source_ref_kind or release.bundle.source_ref_kind
source_url = source_repo_clone_url(source_repo, source_repo_url)
@@ -2734,14 +2605,8 @@ def source_build_plan_for_release(
resolved_source_ref = resolved_source_ref,
source_commit = source_commit,
)
- source_ref = checkout_friendly_ref(
- source_ref_kind, resolved_source_ref or requested_source_ref
- )
- if (
- source_url
- and source_ref
- and source_ref_kind in {"tag", "branch", "pull", "commit"}
- ):
+ source_ref = checkout_friendly_ref(source_ref_kind, resolved_source_ref or requested_source_ref)
+ if source_url and source_ref and source_ref_kind in {"tag", "branch", "pull", "commit"}:
return SourceBuildPlan(
source_url = source_url,
source_ref = source_ref,
@@ -2925,9 +2790,7 @@ def detect_host() -> HostInfo:
# ROCm host as NVIDIA and short-circuit the ROCm path.
try:
listing = run_capture([nvidia_smi, "-L"], timeout = 20)
- gpu_lines = [
- line for line in listing.stdout.splitlines() if line.startswith("GPU ")
- ]
+ gpu_lines = [line for line in listing.stdout.splitlines() if line.startswith("GPU ")]
if gpu_lines:
has_physical_nvidia = True
has_usable_nvidia = visible_device_tokens != []
@@ -3225,9 +3088,7 @@ def detect_torch_cuda_runtime_preference(host: HostInfo) -> CudaRuntimePreferenc
try:
cuda_available = bool(torch.cuda.is_available())
except Exception as exc:
- selection_log.append(
- f"torch_cuda_preference: torch.cuda.is_available() failed: {exc}"
- )
+ selection_log.append(f"torch_cuda_preference: torch.cuda.is_available() failed: {exc}")
return CudaRuntimePreference(runtime_line = None, selection_log = selection_log)
if not cuda_available:
@@ -3315,14 +3176,10 @@ def windows_cuda_attempts(
f"{preferred_runtime_line} unavailable_or_incompatible"
)
else:
- selection_log.append(
- "windows_cuda_selection: no Torch runtime preference available"
- )
+ selection_log.append("windows_cuda_selection: no Torch runtime preference available")
runtime_order.extend(
- runtime_line
- for runtime_line in normal_runtime_lines
- if runtime_line not in runtime_order
+ runtime_line for runtime_line in normal_runtime_lines if runtime_line not in runtime_order
)
# Keep every driver-compatible line reachable as a fallback, so a line gated
# out by the driver version still drops to an older major (cuda13 -> cuda12).
@@ -3346,9 +3203,7 @@ def windows_cuda_attempts(
# Track whatever minor llama.cpp actually ships for this major
# (cuda13 -> 13.1, 13.3, ...). Skip the line when the release has no
# matching asset instead of guessing a now-missing name.
- runtime = _published_windows_cuda_runtime(
- upstream_assets, major, host.driver_cuda_version
- )
+ runtime = _published_windows_cuda_runtime(upstream_assets, major, host.driver_cuda_version)
if runtime is None:
selection_log.append(
f"windows_cuda_selection: no driver-supported asset for {runtime_line}"
@@ -3414,9 +3269,7 @@ def _windows_cuda_attempt_covers_blackwell(attempt: AssetChoice) -> bool:
if attempt.install_kind != "windows-cuda":
return False
m = re.search(r"-bin-win-cuda-(\d+)\.(\d+)-x64\.zip$", attempt.name)
- return (
- m is not None and (int(m.group(1)), int(m.group(2))) >= _BLACKWELL_MIN_TOOLKIT
- )
+ return m is not None and (int(m.group(1)), int(m.group(2))) >= _BLACKWELL_MIN_TOOLKIT
def _pinned_windows_cuda_fallback(
@@ -3441,10 +3294,7 @@ def _pinned_windows_cuda_fallback(
caps = normalize_compute_caps(host.compute_caps)
if not caps or int(caps[-1]) < _BLACKWELL_MIN_SM:
return None
- if any(
- _windows_cuda_attempt_covers_blackwell(attempt)
- for attempt in existing_cuda_attempts
- ):
+ if any(_windows_cuda_attempt_covers_blackwell(attempt) for attempt in existing_cuda_attempts):
return None
tag = _PINNED_BLACKWELL_FALLBACK_TAG
runtime = _PINNED_BLACKWELL_FALLBACK_RUNTIME
@@ -3499,9 +3349,7 @@ def _augment_checksums_with_pin(
def _with_pinned_windows_cuda_fallback(
- host: HostInfo,
- attempts: list[AssetChoice],
- checksums: ApprovedReleaseChecksums,
+ host: HostInfo, attempts: list[AssetChoice], checksums: ApprovedReleaseChecksums
) -> tuple[list[AssetChoice], ApprovedReleaseChecksums]:
"""Insert the Blackwell pin ahead of the Windows CUDA attempts and keep it
through apply_approved_hashes, or return the inputs unchanged when dormant.
@@ -3544,9 +3392,7 @@ def published_windows_cuda_attempts(
selection_log,
)
published_artifacts = [
- artifact
- for artifact in release.artifacts
- if artifact.install_kind == "windows-cuda"
+ artifact for artifact in release.artifacts if artifact.install_kind == "windows-cuda"
]
artifacts_by_runtime: dict[str, list[PublishedLlamaArtifact]] = {}
for artifact in published_artifacts:
@@ -3642,15 +3488,10 @@ def resolve_linux_cuda_choice(
def published_asset_choice_for_kind(
- release: PublishedReleaseBundle,
- install_kind: str,
+ release: PublishedReleaseBundle, install_kind: str
) -> AssetChoice | None:
candidates = sorted(
- (
- artifact
- for artifact in release.artifacts
- if artifact.install_kind == install_kind
- ),
+ (artifact for artifact in release.artifacts if artifact.install_kind == install_kind),
key = lambda artifact: (artifact.rank, artifact.asset_name),
)
for artifact in candidates:
@@ -3666,9 +3507,7 @@ def published_asset_choice_for_kind(
install_kind = install_kind,
runtime_line = artifact.runtime_line,
selection_log = list(release.selection_log)
- + [
- f"published_selection: selected {artifact.asset_name} install_kind={install_kind}"
- ],
+ + [f"published_selection: selected {artifact.asset_name} install_kind={install_kind}"],
)
return None
@@ -3725,11 +3564,7 @@ def _detect_host_rocm_version() -> tuple[int, int] | None:
if result.returncode == 0:
raw = (result.stdout or "").strip().split("\n")[0]
parts = raw.split(".")
- if (
- len(parts) >= 2
- and parts[0].isdigit()
- and parts[1].split("-")[0].isdigit()
- ):
+ if len(parts) >= 2 and parts[0].isdigit() and parts[1].split("-")[0].isdigit():
return int(parts[0]), int(parts[1].split("-")[0])
except Exception:
pass
@@ -3888,9 +3723,7 @@ def resolve_lemonade_rocm_choice(
return None
release_tag = release.get("tag_name") if isinstance(release, dict) else None
if not isinstance(release_tag, str) or not release_tag:
- log(
- f"Unexpected {LEMONADE_ROCM_REPO} release payload; skipping lemonade prebuilt"
- )
+ log(f"Unexpected {LEMONADE_ROCM_REPO} release payload; skipping lemonade prebuilt")
return None
assets = release_asset_map(release)
asset_name = f"llama-{release_tag}-{os_prefix}-rocm-{gfx_family}-x64.zip"
@@ -3986,9 +3819,7 @@ def resolve_upstream_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice
_compatible: list[tuple[tuple[int, ...], str]] = rocm_candidates
if _host_rocm_version is not None:
_compatible = [
- item
- for item in rocm_candidates
- if item[0][:2] <= _host_rocm_version
+ item for item in rocm_candidates if item[0][:2] <= _host_rocm_version
]
if rocm_candidates and not _compatible:
# Fall back to the newest candidate so a source build is
@@ -4052,9 +3883,7 @@ def resolve_upstream_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice
hip_name = f"llama-{llama_tag}-bin-win-hip-radeon-x64.zip"
if hip_name in upstream_assets:
- log(
- f"AMD ROCm detected on Windows -- trying upstream HIP prebuilt {hip_name}"
- )
+ log(f"AMD ROCm detected on Windows -- trying upstream HIP prebuilt {hip_name}")
return AssetChoice(
repo = UPSTREAM_REPO,
tag = llama_tag,
@@ -4063,9 +3892,7 @@ def resolve_upstream_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice
source_label = "upstream",
install_kind = "windows-hip",
)
- log(
- "AMD ROCm detected on Windows but no HIP prebuilt found -- falling back to CPU"
- )
+ log("AMD ROCm detected on Windows but no HIP prebuilt found -- falling back to CPU")
upstream_name = f"llama-{llama_tag}-bin-win-cpu-x64.zip"
if upstream_name not in upstream_assets:
@@ -4105,9 +3932,7 @@ def resolve_upstream_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice
install_kind = "macos-x64",
)
- raise PrebuiltFallback(
- f"no prebuilt policy exists for {host.system} {host.machine}"
- )
+ raise PrebuiltFallback(f"no prebuilt policy exists for {host.system} {host.machine}")
def resolve_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice:
@@ -4185,18 +4010,14 @@ def extract_archive(archive_path: Path, destination: Path) -> None:
normalized = member_name.replace("\\", "/")
member_path = Path(normalized)
if member_path.is_absolute():
- raise PrebuiltFallback(
- f"archive member used an absolute path: {member_name}"
- )
+ raise PrebuiltFallback(f"archive member used an absolute path: {member_name}")
target = (base / member_path).resolve()
base_resolved = base.resolve()
try:
target.relative_to(base_resolved)
except ValueError as exc:
- raise PrebuiltFallback(
- f"archive member escaped destination: {member_name}"
- ) from exc
+ raise PrebuiltFallback(f"archive member escaped destination: {member_name}") from exc
return target
def _try_repair_missing_slash(
@@ -4242,11 +4063,7 @@ def extract_archive(archive_path: Path, destination: Path) -> None:
return candidates[0][len(prefix) :]
def safe_link_target(
- base: Path,
- member_name: str,
- link_name: str,
- target: Path,
- archive_names: set[str],
+ base: Path, member_name: str, link_name: str, target: Path, archive_names: set[str]
) -> tuple[str, Path]:
normalized = link_name.replace("\\", "/")
repaired = _try_repair_missing_slash(member_name, normalized, archive_names)
@@ -4306,9 +4123,7 @@ def extract_archive(archive_path: Path, destination: Path) -> None:
target.parent.mkdir(parents = True, exist_ok = True)
extracted = archive.extractfile(member)
if extracted is None:
- raise PrebuiltFallback(
- f"tar archive entry could not be read: {member.name}"
- )
+ raise PrebuiltFallback(f"tar archive entry could not be read: {member.name}")
with extracted, target.open("wb") as dst:
shutil.copyfileobj(extracted, dst)
@@ -4342,9 +4157,7 @@ def extract_archive(archive_path: Path, destination: Path) -> None:
details = ", ".join(
f"{member.name} -> {member.linkname}" for member, _ in next_round
)
- raise PrebuiltFallback(
- f"tar archive contained unresolved link entries: {details}"
- )
+ raise PrebuiltFallback(f"tar archive contained unresolved link entries: {details}")
unresolved = next_round
destination.mkdir(parents = True, exist_ok = True)
@@ -4358,7 +4171,11 @@ def extract_archive(archive_path: Path, destination: Path) -> None:
def copy_globs(
- source_dir: Path, destination: Path, patterns: list[str], *, required: bool = True
+ source_dir: Path,
+ destination: Path,
+ patterns: list[str],
+ *,
+ required: bool = True,
) -> None:
destination.mkdir(parents = True, exist_ok = True)
matched_sources: dict[str, Path] = {}
@@ -4457,9 +4274,7 @@ def hydrate_source_tree(
for index, source_url in enumerate(source_urls):
try:
if index > 0:
- log(
- f"retrying source tree download from fallback URL: {source_url}"
- )
+ log(f"retrying source tree download from fallback URL: {source_url}")
download_file_verified(
source_url,
archive_path,
@@ -4484,14 +4299,11 @@ def hydrate_source_tree(
source_root / "gguf-py",
]
missing = [
- str(path.relative_to(source_root))
- for path in required_paths
- if not path.exists()
+ str(path.relative_to(source_root)) for path in required_paths if not path.exists()
]
if missing:
raise PrebuiltFallback(
- "upstream source archive was missing required repo files: "
- + ", ".join(missing)
+ "upstream source archive was missing required repo files: " + ", ".join(missing)
)
copy_directory_contents(source_root, install_dir)
except PrebuiltFallback:
@@ -4518,9 +4330,7 @@ def discover_installed_executable(install_dir: Path, executable_name: str) -> Pa
direct = install_dir / executable_name
if direct.exists() and direct.is_file():
return direct
- candidate = next(
- (path for path in install_dir.rglob(executable_name) if path.is_file()), None
- )
+ candidate = next((path for path in install_dir.rglob(executable_name) if path.is_file()), None)
if candidate is None:
raise PrebuiltFallback(f"{executable_name} was not installed")
return candidate
@@ -4550,9 +4360,7 @@ def create_exec_entrypoint(entrypoint: Path, target: Path) -> None:
write_exec_wrapper(entrypoint, target)
-def overlay_directory_for_choice(
- install_dir: Path, choice: AssetChoice, host: HostInfo
-) -> Path:
+def overlay_directory_for_choice(install_dir: Path, choice: AssetChoice, host: HostInfo) -> Path:
if host.is_windows or choice.install_kind.startswith("windows"):
path = install_dir / "build" / "bin" / "Release"
else:
@@ -4590,9 +4398,7 @@ def runtime_patterns_for_choice(choice: AssetChoice) -> list[str]:
"windows-arm64",
}:
return ["llama-server.exe", "llama-quantize.exe", "*.dll"]
- raise PrebuiltFallback(
- f"unsupported install kind for runtime overlay: {choice.install_kind}"
- )
+ raise PrebuiltFallback(f"unsupported install kind for runtime overlay: {choice.install_kind}")
def runtime_subdirs_for_choice(choice: AssetChoice) -> list[str]:
@@ -4801,9 +4607,7 @@ def confirm_install_tree(install_dir: Path, host: HostInfo) -> None:
expected.append(install_dir / "UNSLOTH_PREBUILT_INFO.json")
missing = [str(path) for path in expected if not path.exists()]
if missing:
- raise RuntimeError(
- "activated install was missing expected files: " + ", ".join(missing)
- )
+ raise RuntimeError("activated install was missing expected files: " + ", ".join(missing))
def activate_install_tree(staging_dir: Path, install_dir: Path, host: HostInfo) -> None:
@@ -4926,15 +4730,11 @@ def install_from_archives(
expected_sha256 = choice.runtime_sha256,
label = f"prebuilt runtime archive {choice.runtime_name}",
)
- runtime_extract_dir = Path(
- tempfile.mkdtemp(prefix = "extract-runtime-", dir = work_dir)
- )
+ runtime_extract_dir = Path(tempfile.mkdtemp(prefix = "extract-runtime-", dir = work_dir))
extract_archive(runtime_archive, runtime_extract_dir)
source_dir = extract_dir
overlay_dir = overlay_directory_for_choice(install_dir, choice, host)
- copy_globs(
- source_dir, overlay_dir, runtime_patterns_for_choice(choice), required = True
- )
+ copy_globs(source_dir, overlay_dir, runtime_patterns_for_choice(choice), required = True)
for _subdir in runtime_subdirs_for_choice(choice):
_src_subdir = source_dir / _subdir
if _src_subdir.is_dir():
@@ -4979,9 +4779,7 @@ def install_from_archives(
source_server = build_bin / "llama-server"
source_quantize = build_bin / "llama-quantize"
if not source_server.exists() or not source_quantize.exists():
- raise PrebuiltFallback(
- "unix executables were not installed correctly into build/bin"
- )
+ raise PrebuiltFallback("unix executables were not installed correctly into build/bin")
os.chmod(source_server, 0o755)
os.chmod(source_quantize, 0o755)
@@ -5007,13 +4805,9 @@ def ensure_repo_shape(install_dir: Path) -> None:
install_dir / "convert_hf_to_gguf.py",
install_dir / "gguf-py",
]
- missing = [
- str(path.relative_to(install_dir)) for path in required if not path.exists()
- ]
+ missing = [str(path.relative_to(install_dir)) for path in required if not path.exists()]
if missing:
- raise PrebuiltFallback(
- "hydrated llama.cpp source tree was missing: " + ", ".join(missing)
- )
+ raise PrebuiltFallback("hydrated llama.cpp source tree was missing: " + ", ".join(missing))
def validation_model_cache_path(install_dir: Path) -> Path:
@@ -5028,8 +4822,7 @@ def validated_validation_model_bytes(data: bytes) -> bytes:
digest = hashlib.sha256(data).hexdigest()
if digest != TEST_MODEL_SHA256:
raise RuntimeError(
- "validation model checksum mismatch: "
- f"expected={TEST_MODEL_SHA256} actual={digest}"
+ f"validation model checksum mismatch: expected={TEST_MODEL_SHA256} actual={digest}"
)
return data
@@ -5042,9 +4835,7 @@ def download_validation_model(path: Path, cache_path: Path | None = None) -> Non
data = validated_validation_model_bytes(cache_path.read_bytes())
log(f"using cached tiny GGUF validation model from {cache_path}")
except Exception as exc:
- log(
- f"cached tiny GGUF validation model was invalid; refreshing cache ({exc})"
- )
+ log(f"cached tiny GGUF validation model was invalid; refreshing cache ({exc})")
data = None
if data is None:
log("downloading tiny GGUF validation model")
@@ -5133,9 +4924,7 @@ def dedupe_existing_dirs(paths: Iterable[str | Path]) -> list[str]:
return unique
-def linux_missing_libraries(
- binary_path: Path, *, env: dict[str, str] | None = None
-) -> list[str]:
+def linux_missing_libraries(binary_path: Path, *, env: dict[str, str] | None = None) -> list[str]:
try:
result = run_capture(["ldd", str(binary_path)], timeout = 20, env = env)
except Exception:
@@ -5292,9 +5081,7 @@ def _macho_slice_minos(data: bytes, offset: int) -> tuple[int, int] | None:
return None
-def macho_minimum_macos(
- path: Path, host: HostInfo | None = None
-) -> tuple[int, int] | None:
+def macho_minimum_macos(path: Path, host: HostInfo | None = None) -> tuple[int, int] | None:
"""Minimum macOS (major, minor) a Mach-O binary or dylib requires.
Pure-Python so it works on consumer Macs without the Xcode command line
@@ -5333,9 +5120,7 @@ def macho_minimum_macos(
return None
if host is not None:
want = (
- _CPU_TYPE_ARM64
- if host.is_arm64
- else (_CPU_TYPE_X86_64 if host.is_x86_64 else None)
+ _CPU_TYPE_ARM64 if host.is_arm64 else (_CPU_TYPE_X86_64 if host.is_x86_64 else None)
)
for cputype, minos in slices:
if cputype == want:
@@ -5355,9 +5140,7 @@ def looks_like_macos_incompatibility(text: str) -> bool:
def macos_binary_minos_issues(
- binaries: Iterable[Path],
- install_dir: Path,
- host: HostInfo,
+ binaries: Iterable[Path], install_dir: Path, host: HostInfo
) -> list[str]:
"""Issue strings for every installed Mach-O whose minimum macOS exceeds the
host. Scans the given executables plus every bundled .dylib next to them --
@@ -5387,9 +5170,7 @@ def macos_binary_minos_issues(
def preflight_macos_installed_binaries(
- binaries: Iterable[Path],
- install_dir: Path,
- host: HostInfo,
+ binaries: Iterable[Path], install_dir: Path, host: HostInfo
) -> None:
"""Reject a macos prebuilt whose minimum-OS is newer than the host. The
upstream selector pins a loadable release up front, so here this is the
@@ -5400,15 +5181,12 @@ def preflight_macos_installed_binaries(
issues = macos_binary_minos_issues(binaries, install_dir, host)
if issues:
raise PrebuiltFallback(
- "macos prebuilt requires a newer macOS than this host:\n"
- + "\n".join(issues)
+ "macos prebuilt requires a newer macOS than this host:\n" + "\n".join(issues)
)
def preflight_linux_installed_binaries(
- binaries: Iterable[Path],
- install_dir: Path,
- host: HostInfo,
+ binaries: Iterable[Path], install_dir: Path, host: HostInfo
) -> None:
if not host.is_linux:
return
@@ -5419,18 +5197,14 @@ def preflight_linux_installed_binaries(
missing = linux_missing_libraries(binary_path, env = env)
if not missing:
continue
- runtime_dirs = [
- part for part in env.get("LD_LIBRARY_PATH", "").split(os.pathsep) if part
- ]
+ runtime_dirs = [part for part in env.get("LD_LIBRARY_PATH", "").split(os.pathsep) if part]
issues.append(
f"{binary_path.name}: missing={','.join(missing)} "
f"ld_library_path={','.join(runtime_dirs) if runtime_dirs else 'none'}"
)
if issues:
- raise PrebuiltFallback(
- "linux extracted binary preflight failed:\n" + "\n".join(issues)
- )
+ raise PrebuiltFallback("linux extracted binary preflight failed:\n" + "\n".join(issues))
def glob_paths(*patterns: str) -> list[str]:
@@ -5474,12 +5248,9 @@ def windows_runtime_dirs() -> list[str]:
def windows_runtime_dirs_for_patterns(
- required_patterns: Iterable[str],
- candidate_dirs: Iterable[str] | None = None,
+ required_patterns: Iterable[str], candidate_dirs: Iterable[str] | None = None
) -> list[str]:
- directories = (
- list(candidate_dirs) if candidate_dirs is not None else windows_runtime_dirs()
- )
+ directories = list(candidate_dirs) if candidate_dirs is not None else windows_runtime_dirs()
matching_dirs: list[str] = []
for pattern in required_patterns:
matched_dirs = [
@@ -5523,20 +5294,12 @@ def binary_env(
str(install_dir),
*linux_runtime_dirs(binary_path),
]
- existing = [
- part for part in env.get("LD_LIBRARY_PATH", "").split(os.pathsep) if part
- ]
- env["LD_LIBRARY_PATH"] = os.pathsep.join(
- dedupe_existing_dirs([*ld_dirs, *existing])
- )
+ existing = [part for part in env.get("LD_LIBRARY_PATH", "").split(os.pathsep) if part]
+ env["LD_LIBRARY_PATH"] = os.pathsep.join(dedupe_existing_dirs([*ld_dirs, *existing]))
elif host.is_macos:
dyld_dirs = [str(binary_path.parent), str(install_dir)]
- existing = [
- part for part in env.get("DYLD_LIBRARY_PATH", "").split(os.pathsep) if part
- ]
- env["DYLD_LIBRARY_PATH"] = os.pathsep.join(
- dedupe_existing_dirs([*dyld_dirs, *existing])
- )
+ existing = [part for part in env.get("DYLD_LIBRARY_PATH", "").split(os.pathsep) if part]
+ env["DYLD_LIBRARY_PATH"] = os.pathsep.join(dedupe_existing_dirs([*dyld_dirs, *existing]))
return env
@@ -5558,11 +5321,7 @@ def validate_quantize(
env = binary_env(quantize_path, install_dir, host, runtime_line = runtime_line),
**windows_hidden_subprocess_kwargs(),
)
- if (
- result.returncode != 0
- or not quantized_path.exists()
- or quantized_path.stat().st_size == 0
- ):
+ if result.returncode != 0 or not quantized_path.exists() or quantized_path.stat().st_size == 0:
combined = result.stdout + ("\n" + result.stderr if result.stderr else "")
# Backstop for prebuilts the static minos scan could not read: a dyld
# "built for macOS N" / missing Metal symbol failure means this binary
@@ -5572,9 +5331,7 @@ def validate_quantize(
if looks_like_macos_incompatibility(combined)
else ""
)
- raise PrebuiltFallback(
- prefix + "llama-quantize validation failed:\n" + combined
- )
+ raise PrebuiltFallback(prefix + "llama-quantize validation failed:\n" + combined)
def validate_server(
@@ -5630,9 +5387,7 @@ def validate_server(
# is exercised against the actual hardware rather than the
# CPU fallback. NVIDIA and macOS-arm64 are already covered.
_enable_gpu_layers = (
- host.has_usable_nvidia
- or host.has_rocm
- or (host.is_macos and host.is_arm64)
+ host.has_usable_nvidia or host.has_rocm or (host.is_macos and host.is_arm64)
)
if _enable_gpu_layers:
command.extend(["--n-gpu-layers", "1"])
@@ -5648,9 +5403,7 @@ def validate_server(
stdout = log_handle,
stderr = subprocess.STDOUT,
text = True,
- env = binary_env(
- server_path, install_dir, host, runtime_line = runtime_line
- ),
+ env = binary_env(server_path, install_dir, host, runtime_line = runtime_line),
**windows_hidden_subprocess_kwargs(),
)
deadline = time.time() + 60
@@ -5665,9 +5418,7 @@ def validate_server(
exited_quickly = (
time.time() - startup_started
) <= SERVER_BIND_RETRY_WINDOW_SECONDS
- failure = PrebuiltFallback(
- "llama-server exited during startup:\n" + output
- )
+ failure = PrebuiltFallback("llama-server exited during startup:\n" + output)
if (
port_attempt < SERVER_PORT_BIND_ATTEMPTS
and is_retryable_server_bind_error(
@@ -5684,9 +5435,7 @@ def validate_server(
break
raise failure
- payload = json.dumps({"prompt": "a", "n_predict": 1}).encode(
- "utf-8"
- )
+ payload = json.dumps({"prompt": "a", "n_predict": 1}).encode("utf-8")
request = urllib.request.Request(
f"http://127.0.0.1:{port}/completion",
data = payload,
@@ -5698,9 +5447,7 @@ def validate_server(
response_body = response.read().decode("utf-8", "replace")
if status_code == 200:
return
- last_error = RuntimeError(
- f"unexpected HTTP status {status_code}"
- )
+ last_error = RuntimeError(f"unexpected HTTP status {status_code}")
except urllib.error.HTTPError as exc:
response_body = exc.read().decode("utf-8", "replace")
last_error = exc
@@ -5734,9 +5481,7 @@ def validate_server(
raise PrebuiltFallback("llama-server validation failed unexpectedly")
-def collect_system_report(
- host: HostInfo, choice: AssetChoice | None, install_dir: Path
-) -> str:
+def collect_system_report(host: HostInfo, choice: AssetChoice | None, install_dir: Path) -> str:
lines = [
f"platform={host.system} machine={host.machine}",
f"driver_cuda_version={host.driver_cuda_version}",
@@ -5750,8 +5495,7 @@ def collect_system_report(
if host.is_linux and host.has_physical_nvidia:
runtime_lines, runtime_dirs = detected_linux_runtime_lines()
lines.append(
- "linux_runtime_lines="
- + (",".join(runtime_lines) if runtime_lines else "none")
+ "linux_runtime_lines=" + (",".join(runtime_lines) if runtime_lines else "none")
)
for runtime_line in ("cuda13", "cuda12"):
lines.append(
@@ -5780,10 +5524,7 @@ def collect_system_report(
server_env = binary_env(server_binary, install_dir, host)
lines.append(
"linux_missing_libs="
- + (
- ",".join(linux_missing_libraries(server_binary, env = server_env))
- or "none"
- )
+ + (",".join(linux_missing_libraries(server_binary, env = server_env)) or "none")
)
lines.append(
"linux_runtime_dirs="
@@ -5791,9 +5532,7 @@ def collect_system_report(
",".join(
[
part
- for part in server_env.get("LD_LIBRARY_PATH", "").split(
- os.pathsep
- )
+ for part in server_env.get("LD_LIBRARY_PATH", "").split(os.pathsep)
if part
]
)
@@ -5801,21 +5540,16 @@ def collect_system_report(
)
)
try:
- ldd = run_capture(
- ["ldd", str(server_binary)], timeout = 20, env = server_env
- )
+ ldd = run_capture(["ldd", str(server_binary)], timeout = 20, env = server_env)
lines.append("ldd llama-server:")
lines.append((ldd.stdout + ldd.stderr).strip())
except Exception as exc:
lines.append(f"ldd error: {exc}")
elif host.is_windows:
- lines.append(
- "windows_runtime_dirs=" + (",".join(windows_runtime_dirs()) or "none")
- )
+ lines.append("windows_runtime_dirs=" + (",".join(windows_runtime_dirs()) or "none"))
runtime_lines, runtime_dirs = detected_windows_runtime_lines()
lines.append(
- "windows_runtime_lines="
- + (",".join(runtime_lines) if runtime_lines else "none")
+ "windows_runtime_lines=" + (",".join(runtime_lines) if runtime_lines else "none")
)
for runtime_line in ("cuda13", "cuda12"):
lines.append(
@@ -5840,8 +5574,7 @@ def collect_system_report(
def apply_approved_hashes(
- attempts: Iterable[AssetChoice],
- checksums: ApprovedReleaseChecksums,
+ attempts: Iterable[AssetChoice], checksums: ApprovedReleaseChecksums
) -> list[AssetChoice]:
def approved_hash_for_attempt(attempt: AssetChoice) -> ApprovedArtifactHash | None:
candidate_names = [attempt.name]
@@ -5949,8 +5682,7 @@ def preferred_source_archive(
def selected_source_archive_metadata(
- checksums: ApprovedReleaseChecksums,
- llama_tag: str,
+ checksums: ApprovedReleaseChecksums, llama_tag: str
) -> tuple[str, str | None]:
_source_repo, _source_ref, source_archive, _exact_source = preferred_source_archive(
checksums, llama_tag
@@ -5961,10 +5693,7 @@ def selected_source_archive_metadata(
def resolve_install_attempts(
- llama_tag: str,
- host: HostInfo,
- published_repo: str,
- published_release_tag: str,
+ llama_tag: str, host: HostInfo, published_repo: str, published_release_tag: str
) -> tuple[str, str, list[AssetChoice], ApprovedReleaseChecksums]:
requested_tag, plans = resolve_install_release_plans(
llama_tag,
@@ -5987,17 +5716,11 @@ def resolve_install_release_plans(
max_release_fallbacks: int = DEFAULT_MAX_PREBUILT_RELEASE_FALLBACKS,
) -> tuple[str, list[InstallReleasePlan]]:
requested_tag = normalized_requested_llama_tag(llama_tag)
- allow_older_release_fallback = (
- requested_tag == "latest" and not published_release_tag
- )
+ allow_older_release_fallback = requested_tag == "latest" and not published_release_tag
release_limit = max(1, max_release_fallbacks)
# macOS may need to walk past a run of too-new prebuilts. Only when the host
# version is known; otherwise keep the default (cannot tell up front).
- if (
- host.is_macos
- and allow_older_release_fallback
- and host.macos_version is not None
- ):
+ if host.is_macos and allow_older_release_fallback and host.macos_version is not None:
release_limit = max(release_limit, DEFAULT_MAX_MACOS_RELEASE_FALLBACKS)
plans: list[InstallReleasePlan] = []
last_error: PrebuiltFallback | None = None
@@ -6013,9 +5736,7 @@ def resolve_install_release_plans(
try:
if host.is_linux and host.is_x86_64 and host.has_usable_nvidia:
linux_cuda_selection = resolve_linux_cuda_choice(host, bundle)
- attempts = apply_approved_hashes(
- linux_cuda_selection.attempts, checksums
- )
+ attempts = apply_approved_hashes(linux_cuda_selection.attempts, checksums)
if not attempts:
raise PrebuiltFallback("no compatible Linux CUDA asset was found")
log_lines(linux_cuda_selection.selection_log)
@@ -6117,9 +5838,7 @@ def write_prebuilt_metadata(
"prebuilt_fallback_used": prebuilt_fallback_used,
"installed_at_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
}
- (install_dir / "UNSLOTH_PREBUILT_INFO.json").write_text(
- json.dumps(metadata, indent = 2) + "\n"
- )
+ (install_dir / "UNSLOTH_PREBUILT_INFO.json").write_text(json.dumps(metadata, indent = 2) + "\n")
def expected_install_fingerprint(
@@ -6236,9 +5955,7 @@ def install_runtime_dir(install_dir: Path, host: HostInfo) -> Path:
return install_dir / "build" / "bin"
-def runtime_payload_is_healthy(
- install_dir: Path, host: HostInfo, choice: AssetChoice
-) -> bool:
+def runtime_payload_is_healthy(install_dir: Path, host: HostInfo, choice: AssetChoice) -> bool:
runtime_dir = install_runtime_dir(install_dir, host)
if not runtime_dir.exists():
return False
@@ -6326,9 +6043,7 @@ def existing_install_matches_choice(
def existing_install_matches_plan(
- install_dir: Path,
- host: HostInfo,
- plan: InstallReleasePlan,
+ install_dir: Path, host: HostInfo, plan: InstallReleasePlan
) -> bool:
if not plan.attempts:
return False
@@ -6360,9 +6075,7 @@ def validate_prebuilt_choice(
approved_checksums, llama_tag
)
if exact_source:
- log(
- f"hydrating exact llama.cpp source for {source_repo}@{source_ref} into {install_dir}"
- )
+ log(f"hydrating exact llama.cpp source for {source_repo}@{source_ref} into {install_dir}")
else:
log(f"hydrating upstream llama.cpp source for {llama_tag} into {install_dir}")
hydrate_source_tree(
@@ -6379,9 +6092,7 @@ def validate_prebuilt_choice(
exact_source = exact_source,
)
log(f"overlaying prebuilt bundle {choice.name} into {install_dir}")
- server_path, quantize_path = install_from_archives(
- choice, host, install_dir, work_dir
- )
+ server_path, quantize_path = install_from_archives(choice, host, install_dir, work_dir)
preflight_linux_installed_binaries((server_path, quantize_path), install_dir, host)
preflight_macos_installed_binaries((server_path, quantize_path), install_dir, host)
ensure_repo_shape(install_dir)
@@ -6539,9 +6250,7 @@ def install_prebuilt(
published_repo,
published_release_tag,
)
- if release_plans and existing_install_matches_plan(
- install_dir, host, release_plans[0]
- ):
+ if release_plans and existing_install_matches_plan(install_dir, host, release_plans[0]):
current = release_plans[0]
log(
"existing llama.cpp install already matches selected release "
@@ -6551,9 +6260,7 @@ def install_prebuilt(
with tempfile.TemporaryDirectory(prefix = "unsloth-llama-prebuilt-") as tmp:
work_dir = Path(tmp)
probe_path = work_dir / "stories260K.gguf"
- download_validation_model(
- probe_path, validation_model_cache_path(install_dir)
- )
+ download_validation_model(probe_path, validation_model_cache_path(install_dir))
release_count = len(release_plans)
for release_index, plan in enumerate(release_plans):
choice = plan.attempts[0]
@@ -6759,9 +6466,7 @@ def main() -> int:
)
emit_resolver_output(
{
- "requested_tag": normalized_requested_llama_tag(
- args.resolve_install_tag
- ),
+ "requested_tag": normalized_requested_llama_tag(args.resolve_install_tag),
"llama_tag": resolved,
},
output_format = args.output_format,
@@ -6776,9 +6481,7 @@ def main() -> int:
)
emit_resolver_output(
{
- "requested_tag": normalized_requested_llama_tag(
- args.resolve_source_build
- ),
+ "requested_tag": normalized_requested_llama_tag(args.resolve_source_build),
"source_url": plan.source_url,
"source_ref_kind": plan.source_ref_kind,
"source_ref": plan.source_ref,
diff --git a/studio/install_python_stack.py b/studio/install_python_stack.py
index da202f7ce6..5ff8e8572b 100644
--- a/studio/install_python_stack.py
+++ b/studio/install_python_stack.py
@@ -176,7 +176,6 @@ def _detect_rocm_version() -> tuple[int, int] | None:
)
if result.returncode == 0:
import re
-
m = re.search(r"ROCm version:\s*(\d+)\.(\d+)", result.stdout)
if m:
return int(m.group(1)), int(m.group(2))
@@ -196,11 +195,7 @@ def _detect_rocm_version() -> tuple[int, int] | None:
if result.returncode == 0:
raw = result.stdout.decode().strip().split("\n")[0]
parts = raw.split(".")
- if (
- len(parts) >= 2
- and parts[0].isdigit()
- and parts[1].split("-")[0].isdigit()
- ):
+ if len(parts) >= 2 and parts[0].isdigit() and parts[1].split("-")[0].isdigit():
return int(parts[0]), int(parts[1].split("-")[0])
except Exception:
pass
@@ -309,8 +304,7 @@ def _detect_windows_gfx_arch() -> str | None:
# findall picks every gcnArchName line so multi-GPU hosts
# are enumerable and HIP_VISIBLE_DEVICES selects correctly.
_tokens = [
- t.strip().lower()
- for t in re.findall(r"(?im)^\s*gcnArchName\s*:\s*(\S+)", text)
+ t.strip().lower() for t in re.findall(r"(?im)^\s*gcnArchName\s*:\s*(\S+)", text)
]
_pick = _dedup_pick(_tokens)
if _pick:
@@ -612,9 +606,7 @@ def _ensure_rocm_torch() -> None:
if not _torch_already_rocm:
index_url = _windows_rocm_index_url(gfx_arch)
if index_url is None:
- print(
- f" No AMD Windows torch index for GPU arch {gfx_arch} -- skipping"
- )
+ print(f" No AMD Windows torch index for GPU arch {gfx_arch} -- skipping")
return
print(f" {gfx_arch} (Windows) -- installing torch from {index_url}")
pip_install(
@@ -688,9 +680,7 @@ def _ensure_rocm_torch() -> None:
except (OSError, subprocess.TimeoutExpired):
probe = None
has_hip_torch = (
- probe is not None
- and probe.returncode == 0
- and probe.stdout.decode().strip() != ""
+ probe is not None and probe.returncode == 0 and probe.stdout.decode().strip() != ""
)
rocm_torch_ready = has_hip_torch
@@ -714,14 +704,11 @@ def _ensure_rocm_torch() -> None:
# specific index into gfx_codes, use that gfx; else default to the
# first listed GPU. Skip the override unless the resolved GPU is
# Strix.
- _runtime_gfx = (
- gfx_codes[_pick_visible_index(len(gfx_codes))] if gfx_codes else None
- )
+ _runtime_gfx = gfx_codes[_pick_visible_index(len(gfx_codes))] if gfx_codes else None
if _runtime_gfx in _strix_gfx:
_selected_gfx = _runtime_gfx
_amd_mirror = (
- os.environ.get("UNSLOTH_AMD_ROCM_MIRROR")
- or "https://repo.amd.com/rocm/whl"
+ os.environ.get("UNSLOTH_AMD_ROCM_MIRROR") or "https://repo.amd.com/rocm/whl"
).rstrip("/")
_strix_override_url = f"{_amd_mirror}/{_selected_gfx}/"
_strix_override_pkgs = (
@@ -782,10 +769,7 @@ def _ensure_rocm_torch() -> None:
None,
)
if tag is None:
- print(
- f" No PyTorch wheel for ROCm {ver[0]}.{ver[1]} -- "
- f"skipping torch reinstall"
- )
+ print(f" No PyTorch wheel for ROCm {ver[0]}.{ver[1]} -- " f"skipping torch reinstall")
else:
index_url = f"{_PYTORCH_WHL_BASE}/{tag}"
print(f" ROCm {ver[0]}.{ver[1]} -- installing torch from {index_url}")
@@ -922,9 +906,7 @@ CONSTRAINTS = SINGLE_ENV / "constraints.txt"
LOCAL_DD_UNSTRUCTURED_PLUGIN = (
SCRIPT_DIR / "backend" / "plugins" / "data-designer-unstructured-seed"
)
-LOCAL_DD_GITHUB_PLUGIN = (
- SCRIPT_DIR / "backend" / "plugins" / "data-designer-github-repo-seed"
-)
+LOCAL_DD_GITHUB_PLUGIN = SCRIPT_DIR / "backend" / "plugins" / "data-designer-github-repo-seed"
# Apple Silicon: override mlx-vlm/mlx-lm's transformers pin (see overrides file).
_MLX_OVERRIDES = SINGLE_ENV / "overrides-darwin-arm64.txt"
@@ -1026,7 +1008,11 @@ def _title(msg: str) -> str:
_RULE = "\u2500" * 52
-def _step(label: str, value: str, color_fn = None) -> None:
+def _step(
+ label: str,
+ value: str,
+ color_fn = None,
+) -> None:
"""Print a single step line in the column format."""
if color_fn is None:
color_fn = _green
@@ -1046,16 +1032,17 @@ def _progress(label: str) -> None:
pad = " " * (_COL - len(_LABEL))
end = "\n" if _STEP >= _TOTAL else ""
try:
- sys.stdout.write(
- f"\r {_dim(_LABEL)}{pad}[{bar}] {_STEP:2}/{_TOTAL} {label:<20}{end}"
- )
+ sys.stdout.write(f"\r {_dim(_LABEL)}{pad}[{bar}] {_STEP:2}/{_TOTAL} {label:<20}{end}")
sys.stdout.flush()
except OSError:
pass
def run(
- label: str, cmd: list[str], *, quiet: bool = True
+ label: str,
+ cmd: list[str],
+ *,
+ quiet: bool = True,
) -> subprocess.CompletedProcess[bytes]:
"""Run a command; on failure print output and exit."""
if VERBOSE:
@@ -1107,9 +1094,7 @@ def _build_flash_attn_wheel_url(env: dict[str, str]) -> str | None:
return flash_attn_wheel_url(env)
-def _print_optional_install_failure(
- label: str, result: subprocess.CompletedProcess[str]
-) -> None:
+def _print_optional_install_failure(label: str, result: subprocess.CompletedProcess[str]) -> None:
_step("warning", f"{label} failed (exit code {result.returncode})", _cyan)
if result.stdout:
print(result.stdout.strip())
@@ -1204,9 +1189,7 @@ def _filter_requirements(req: Path, skip: set[str]) -> Path:
"""Return a temp copy of a requirements file with certain packages removed."""
lines = req.read_text(encoding = "utf-8").splitlines(keepends = True)
filtered = [
- line
- for line in lines
- if not any(line.strip().lower().startswith(pkg) for pkg in skip)
+ line for line in lines if not any(line.strip().lower().startswith(pkg) for pkg in skip)
]
tmp = tempfile.NamedTemporaryFile(
mode = "w",
@@ -1416,9 +1399,7 @@ def install_python_stack() -> int:
if not IS_MACOS and not NO_TORCH:
base_total += 1 # ROCm torch check (line 1526) -- all non-macOS platforms
if not IS_WINDOWS:
- base_total += (
- 2 # flash-attn (line 1620) + ROCm torch final (line 1705) -- Linux only
- )
+ base_total += 2 # flash-attn (line 1620) + ROCm torch final (line 1705) -- Linux only
_TOTAL = (base_total - 1) if skip_base else base_total
# 1. Try to use uv for faster installs (must happen before pip upgrade
diff --git a/tests/_zoo_aggressive_cuda_spoof.py b/tests/_zoo_aggressive_cuda_spoof.py
index eaafe445fb..9111aa9519 100644
--- a/tests/_zoo_aggressive_cuda_spoof.py
+++ b/tests/_zoo_aggressive_cuda_spoof.py
@@ -159,7 +159,11 @@ def apply() -> None:
if _orig is None:
continue
- def _wrap(*args: Any, _orig = _orig, **kwargs: Any):
+ def _wrap(
+ *args: Any,
+ _orig = _orig,
+ **kwargs: Any,
+ ):
kwargs.pop("pin_memory", None)
return _orig(*args, **kwargs)
diff --git a/tests/conftest.py b/tests/conftest.py
index 2d7038d5d4..ad58cb9706 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -105,13 +105,11 @@ def _patch_torch_cuda_for_import() -> None:
CPU like normal."""
try:
import torch.cuda.memory as _cuda_memory # type: ignore
-
_cuda_memory.mem_get_info = lambda *a, **k: (0, 80 * 1024**3)
except Exception:
pass
try:
import torch
-
torch.cuda.get_device_capability = lambda *a, **k: (8, 0)
torch.cuda.is_bf16_supported = lambda *a, **k: True
except Exception:
diff --git a/tests/python/conftest.py b/tests/python/conftest.py
index 9129e384e5..f7b125edf6 100644
--- a/tests/python/conftest.py
+++ b/tests/python/conftest.py
@@ -2,9 +2,5 @@
def pytest_configure(config):
- config.addinivalue_line(
- "markers", "server: heavyweight tests requiring studio venv"
- )
- config.addinivalue_line(
- "markers", "e2e: end-to-end tests requiring network and venv creation"
- )
+ config.addinivalue_line("markers", "server: heavyweight tests requiring studio venv")
+ config.addinivalue_line("markers", "e2e: end-to-end tests requiring network and venv creation")
diff --git a/tests/python/test_cross_platform_parity.py b/tests/python/test_cross_platform_parity.py
index 7b9868c1f2..6c504579ce 100644
--- a/tests/python/test_cross_platform_parity.py
+++ b/tests/python/test_cross_platform_parity.py
@@ -28,17 +28,11 @@ class TestNoTorchBackendAutoInInstallSh:
for i, line in enumerate(lines):
if fallback_start is None and "GPU detection failed" in line:
fallback_start = i
- elif (
- fallback_start is not None
- and fallback_end is None
- and line.strip() == "fi"
- ):
+ elif fallback_start is not None and fallback_end is None and line.strip() == "fi":
fallback_end = i
break
fallback_range = (
- range(fallback_start or 0, (fallback_end or 0) + 1)
- if fallback_start
- else range(0)
+ range(fallback_start or 0, (fallback_end or 0) + 1) if fallback_start else range(0)
)
matches = [
diff --git a/tests/python/test_dpo_vision_processor_passthrough.py b/tests/python/test_dpo_vision_processor_passthrough.py
index a4f2e2e12a..a320cab935 100644
--- a/tests/python/test_dpo_vision_processor_passthrough.py
+++ b/tests/python/test_dpo_vision_processor_passthrough.py
@@ -33,7 +33,11 @@ class _Tok:
eos_token_id = 99
bos_token_id = None
- def __call__(self, t, add_special_tokens = False):
+ def __call__(
+ self,
+ t,
+ add_special_tokens = False,
+ ):
return {"input_ids": [10]}
@@ -46,7 +50,12 @@ class _Capture:
self.last_text = None
self.last_images = "__sentinel__"
- def __call__(self, images = None, text = None, add_special_tokens = False):
+ def __call__(
+ self,
+ images = None,
+ text = None,
+ add_special_tokens = False,
+ ):
self.last_text = text
self.last_images = images
out = {"input_ids": [[1, 2]]}
diff --git a/tests/python/test_e2e_no_torch_sandbox.py b/tests/python/test_e2e_no_torch_sandbox.py
index f36f69201d..382d0afe4e 100644
--- a/tests/python/test_e2e_no_torch_sandbox.py
+++ b/tests/python/test_e2e_no_torch_sandbox.py
@@ -247,12 +247,8 @@ class TestBeforeAfterImportChain:
exec(source)
""")
result = _run_in_sandbox(no_torch_venv, code)
- assert (
- result.returncode != 0
- ), "BEFORE chat_templates.py should crash without torch"
- assert (
- b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
- )
+ assert result.returncode != 0, "BEFORE chat_templates.py should crash without torch"
+ assert b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
def test_before_data_collators_crashes(self, no_torch_venv, sandbox_dir):
"""BEFORE: data_collators.py with top-level 'import torch' crashes."""
@@ -270,12 +266,8 @@ class TestBeforeAfterImportChain:
exec(open({str(before_file)!r}).read())
""")
result = _run_in_sandbox(no_torch_venv, code)
- assert (
- result.returncode != 0
- ), "BEFORE data_collators.py should crash without torch"
- assert (
- b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
- )
+ assert result.returncode != 0, "BEFORE data_collators.py should crash without torch"
+ assert b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
def test_before_full_import_chain_crashes(self, no_torch_venv, sandbox_dir):
"""BEFORE: full utils/datasets/ package with top-level torch imports crashes."""
@@ -320,12 +312,8 @@ class TestBeforeAfterImportChain:
from utils.datasets import detect_dataset_format
""")
result = _run_in_sandbox(no_torch_venv, code)
- assert (
- result.returncode != 0
- ), "BEFORE full import chain should crash without torch"
- assert (
- b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
- )
+ assert result.returncode != 0, "BEFORE full import chain should crash without torch"
+ assert b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
# -- AFTER: succeeds --
@@ -539,9 +527,7 @@ class TestEdgeCasesBrokenTorch:
print("OK: data_collators works despite broken torch on sys.path")
""")
result = _run_in_sandbox(no_torch_venv, code)
- assert (
- result.returncode == 0
- ), f"Should work with broken torch:\n{result.stderr.decode()}"
+ assert result.returncode == 0, f"Should work with broken torch:\n{result.stderr.decode()}"
assert b"OK:" in result.stdout
def test_torch_import_error_hardware_fallback(self, no_torch_venv, sandbox_dir):
@@ -604,14 +590,10 @@ class TestEdgeCasesBrokenTorch:
print("OK: detect_hardware returned CPU with fake torch (no CUDA)")
""")
result = _run_in_sandbox(no_torch_venv, code)
- assert (
- result.returncode == 0
- ), f"Should fall back to CPU:\n{result.stderr.decode()}"
+ assert result.returncode == 0, f"Should fall back to CPU:\n{result.stderr.decode()}"
assert b"OK:" in result.stdout
- def test_lazy_torch_fails_at_call_time_not_import_time(
- self, no_torch_venv, sandbox_dir
- ):
+ def test_lazy_torch_fails_at_call_time_not_import_time(self, no_torch_venv, sandbox_dir):
"""apply_chat_template_to_dataset is importable without torch.
Calling the alpaca branch triggers the lazy 'from torch.utils.data' inside
@@ -657,9 +639,7 @@ class TestEdgeCasesBrokenTorch:
print("OK: call succeeded (unexpected but not a crash)")
""")
result = _run_in_sandbox(no_torch_venv, code)
- assert (
- result.returncode == 0
- ), f"Should not crash at import time:\n{result.stderr.decode()}"
+ assert result.returncode == 0, f"Should not crash at import time:\n{result.stderr.decode()}"
assert b"OK: import succeeded" in result.stdout
@@ -1011,9 +991,7 @@ class TestInstallPythonStackFiltering:
source = Path(ips.__file__).read_text(encoding = "utf-8")
# NO_TORCH guard before overrides
- assert (
- "if NO_TORCH:" in source
- ), "NO_TORCH guard not found in install_python_stack.py"
+ assert "if NO_TORCH:" in source, "NO_TORCH guard not found in install_python_stack.py"
# macOS guard for triton
assert (
@@ -1037,7 +1015,6 @@ def _studio_venv_python() -> Path | None:
def _server_port() -> int:
"""Find an available port for the test server."""
import socket
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("", 0))
return s.getsockname()[1]
@@ -1117,9 +1094,7 @@ class TestLiveServerStartup:
for _ in range(30):
time.sleep(1)
try:
- resp = urllib.request.urlopen(
- f"http://127.0.0.1:{port}/api/health", timeout = 2
- )
+ resp = urllib.request.urlopen(f"http://127.0.0.1:{port}/api/health", timeout = 2)
if resp.status == 200:
ready = True
break
@@ -1143,12 +1118,8 @@ class TestLiveServerStartup:
capture_output = True,
timeout = 300,
)
- server_output = stdout.decode(errors = "replace") + stderr.decode(
- errors = "replace"
- )
- pytest.skip(
- f"Server failed to start within 30 seconds. Output:\n{server_output}"
- )
+ server_output = stdout.decode(errors = "replace") + stderr.decode(errors = "replace")
+ pytest.skip(f"Server failed to start within 30 seconds. Output:\n{server_output}")
yield proc, port
@@ -1192,9 +1163,7 @@ class TestLiveServerStartup:
import urllib.request
_, port = server_process
- resp = urllib.request.urlopen(
- f"http://127.0.0.1:{port}/openapi.json", timeout = 5
- )
+ resp = urllib.request.urlopen(f"http://127.0.0.1:{port}/openapi.json", timeout = 5)
spec = json.loads(resp.read())
assert (
len(spec.get("paths", {})) >= 20
diff --git a/tests/python/test_fast_sentence_transformer_redirect_lifecycle.py b/tests/python/test_fast_sentence_transformer_redirect_lifecycle.py
index 31d86b09a4..3f5f235aae 100644
--- a/tests/python/test_fast_sentence_transformer_redirect_lifecycle.py
+++ b/tests/python/test_fast_sentence_transformer_redirect_lifecycle.py
@@ -62,7 +62,6 @@ class _RecordingTransformerOk:
def __init__(self, model_name, **kwargs):
from transformers import AutoModel, AutoProcessor, AutoTokenizer
-
type(self).last_calls = {
"model": AutoModel.from_pretrained(model_name),
"processor": AutoProcessor.from_pretrained(model_name),
@@ -73,7 +72,6 @@ class _RecordingTransformerOk:
class _RaisingTransformer:
def __init__(self, *a, **kw):
from transformers import AutoModel
-
AutoModel.from_pretrained(a[0] if a else kw.get("model_name_or_path"))
raise RuntimeError("simulated init failure")
@@ -129,18 +127,10 @@ def _build_driver(transformer_class):
return model if is_requested_model_name(a, kw) else original_model(*a, **kw)
def return_existing_tokenizer(*a, **kw):
- return (
- tokenizer
- if is_requested_model_name(a, kw)
- else original_tokenizer(*a, **kw)
- )
+ return tokenizer if is_requested_model_name(a, kw) else original_tokenizer(*a, **kw)
def return_existing_processor(*a, **kw):
- return (
- tokenizer
- if is_requested_model_name(a, kw)
- else original_processor(*a, **kw)
- )
+ return tokenizer if is_requested_model_name(a, kw) else original_processor(*a, **kw)
try:
AutoModel.from_pretrained = return_existing_model
@@ -190,7 +180,6 @@ def test_redirect_passes_through_for_other_model_names():
def __init__(self, model_name, **kw):
from transformers import AutoModel
-
type(self).captured = AutoModel.from_pretrained("some-other/aux-model")
driver, *_ = _build_driver(_OtherNameTransformer)
@@ -210,7 +199,6 @@ def test_is_requested_model_name_handles_pathlib_path(tmp_path):
def __init__(self, model_name, **kw):
from transformers import AutoModel
-
type(self).last_calls = AutoModel.from_pretrained(pathlib.Path(model_name))
driver, *_ = _build_driver(_PathTransformer)
@@ -228,7 +216,6 @@ def test_is_requested_model_name_trailing_slash_local_path(tmp_path):
def __init__(self, model_name, **kw):
from transformers import AutoModel
-
type(self).last_calls = AutoModel.from_pretrained(str(target) + "/")
driver, *_ = _build_driver(_SlashTransformer)
@@ -243,7 +230,6 @@ def test_is_requested_model_name_returns_false_when_no_identifier():
class _NoNameTransformer:
def __init__(self, model_name, **kw):
from transformers import AutoModel
-
captured["args"] = AutoModel.from_pretrained(some_other_kwarg = "x")
driver, *_ = _build_driver(_NoNameTransformer)
diff --git a/tests/python/test_flash_attn_install_python_stack.py b/tests/python/test_flash_attn_install_python_stack.py
index 49f4350a7b..26ff03505a 100644
--- a/tests/python/test_flash_attn_install_python_stack.py
+++ b/tests/python/test_flash_attn_install_python_stack.py
@@ -33,64 +33,42 @@ class TestHasBlackwellGpu:
def test_returns_true_for_sm_100(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
- mock.patch.object(
- wheel_utils.subprocess, "run", return_value = _smi_result("10.0\n")
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
+ mock.patch.object(wheel_utils.subprocess, "run", return_value = _smi_result("10.0\n")),
):
assert wheel_utils.has_blackwell_gpu() is True
def test_returns_true_for_sm_120(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
- mock.patch.object(
- wheel_utils.subprocess, "run", return_value = _smi_result("12.0\n")
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
+ mock.patch.object(wheel_utils.subprocess, "run", return_value = _smi_result("12.0\n")),
):
assert wheel_utils.has_blackwell_gpu() is True
def test_returns_true_for_sm_121(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
- mock.patch.object(
- wheel_utils.subprocess, "run", return_value = _smi_result("12.1\n")
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
+ mock.patch.object(wheel_utils.subprocess, "run", return_value = _smi_result("12.1\n")),
):
assert wheel_utils.has_blackwell_gpu() is True
def test_returns_false_for_sm_90(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
- mock.patch.object(
- wheel_utils.subprocess, "run", return_value = _smi_result("9.0\n")
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
+ mock.patch.object(wheel_utils.subprocess, "run", return_value = _smi_result("9.0\n")),
):
assert wheel_utils.has_blackwell_gpu() is False
def test_returns_false_for_sm_89(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
- mock.patch.object(
- wheel_utils.subprocess, "run", return_value = _smi_result("8.9\n")
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
+ mock.patch.object(wheel_utils.subprocess, "run", return_value = _smi_result("8.9\n")),
):
assert wheel_utils.has_blackwell_gpu() is False
def test_mixed_gpus_with_one_blackwell_returns_true(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
mock.patch.object(
wheel_utils.subprocess,
"run",
@@ -101,9 +79,7 @@ class TestHasBlackwellGpu:
def test_returns_false_when_nvidia_smi_fails(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
mock.patch.object(
wheel_utils.subprocess,
"run",
@@ -114,9 +90,7 @@ class TestHasBlackwellGpu:
def test_returns_false_on_subprocess_timeout(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
mock.patch.object(
wheel_utils.subprocess,
"run",
@@ -127,9 +101,7 @@ class TestHasBlackwellGpu:
def test_returns_false_on_malformed_output(self):
with (
- mock.patch.object(
- wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"
- ),
+ mock.patch.object(wheel_utils.shutil, "which", return_value = "/usr/bin/nvidia-smi"),
mock.patch.object(
wheel_utils.subprocess,
"run",
@@ -161,10 +133,7 @@ class TestFlashAttnWheelSelection:
)
assert url is not None
assert "v2.8.1" in url
- assert (
- "flash_attn-2.8.1+cu12torch2.10cxx11abiTRUE-cp313-cp313-linux_x86_64.whl"
- in url
- )
+ assert "flash_attn-2.8.1+cu12torch2.10cxx11abiTRUE-cp313-cp313-linux_x86_64.whl" in url
def test_missing_cuda_major_disables_wheel_lookup(self):
assert (
@@ -262,7 +231,11 @@ class TestEnsureFlashAttn:
step_messages: list[tuple[str, str]] = []
printed_failures: list[str] = []
- def fake_step(label: str, value: str, color_fn = None):
+ def fake_step(
+ label: str,
+ value: str,
+ color_fn = None,
+ ):
step_messages.append((label, value))
with (
@@ -313,7 +286,11 @@ class TestEnsureFlashAttn:
def test_wheel_missing_skips_install_at_setup_time(self):
step_messages: list[tuple[str, str]] = []
- def fake_step(label: str, value: str, color_fn = None):
+ def fake_step(
+ label: str,
+ value: str,
+ color_fn = None,
+ ):
step_messages.append((label, value))
with (
@@ -339,10 +316,7 @@ class TestEnsureFlashAttn:
ips._ensure_flash_attn()
mock_install_wheel.assert_not_called()
- assert (
- "warning",
- "No published flash-attn prebuilt wheel found",
- ) in step_messages
+ assert ("warning", "No published flash-attn prebuilt wheel found") in step_messages
def test_skip_env_disables_setup_install(self):
with (
@@ -362,7 +336,11 @@ class TestEnsureFlashAttn:
def test_blackwell_gpu_skips_install_with_warning(self):
step_messages: list[tuple[str, str]] = []
- def fake_step(label: str, value: str, color_fn = None):
+ def fake_step(
+ label: str,
+ value: str,
+ color_fn = None,
+ ):
step_messages.append((label, value))
with (
@@ -379,14 +357,16 @@ class TestEnsureFlashAttn:
mock_probe.assert_not_called()
mock_install_wheel.assert_not_called()
- assert any(
- label == "warning" and "Blackwell" in msg for label, msg in step_messages
- )
+ assert any(label == "warning" and "Blackwell" in msg for label, msg in step_messages)
def test_blackwell_gpu_on_windows_emits_blackwell_warning(self):
step_messages: list[tuple[str, str]] = []
- def fake_step(label: str, value: str, color_fn = None):
+ def fake_step(
+ label: str,
+ value: str,
+ color_fn = None,
+ ):
step_messages.append((label, value))
with (
@@ -403,14 +383,16 @@ class TestEnsureFlashAttn:
mock_probe.assert_not_called()
mock_install_wheel.assert_not_called()
- assert any(
- label == "warning" and "Blackwell" in msg for label, msg in step_messages
- )
+ assert any(label == "warning" and "Blackwell" in msg for label, msg in step_messages)
def test_non_blackwell_windows_does_not_emit_blackwell_warning(self):
step_messages: list[tuple[str, str]] = []
- def fake_step(label: str, value: str, color_fn = None):
+ def fake_step(
+ label: str,
+ value: str,
+ color_fn = None,
+ ):
step_messages.append((label, value))
with (
@@ -453,9 +435,7 @@ class TestInstallPythonStackFlashAttnIntegration:
mock.patch("subprocess.run", side_effect = fake_run),
mock.patch.object(ips, "_has_usable_nvidia_gpu", return_value = False),
mock.patch.object(ips, "_has_rocm_gpu", return_value = False),
- mock.patch.object(
- ips, "LOCAL_DD_UNSTRUCTURED_PLUGIN", Path("/fake/plugin")
- ),
+ mock.patch.object(ips, "LOCAL_DD_UNSTRUCTURED_PLUGIN", Path("/fake/plugin")),
mock.patch("pathlib.Path.is_dir", return_value = True),
mock.patch("pathlib.Path.is_file", return_value = True),
mock.patch.dict(os.environ, {"SKIP_STUDIO_BASE": "1"}, clear = False),
diff --git a/tests/python/test_gpu_init_ldconfig_guard.py b/tests/python/test_gpu_init_ldconfig_guard.py
index 081a6132b4..248bb84faa 100644
--- a/tests/python/test_gpu_init_ldconfig_guard.py
+++ b/tests/python/test_gpu_init_ldconfig_guard.py
@@ -19,9 +19,7 @@ def _find_geteuid_guard(tree: ast.AST):
def test_gpu_init_has_geteuid_guard():
tree = ast.parse(GPU_INIT.read_text())
guard = _find_geteuid_guard(tree)
- assert (
- guard is not None
- ), "_gpu_init.py must guard ldconfig recovery on os.geteuid()"
+ assert guard is not None, "_gpu_init.py must guard ldconfig recovery on os.geteuid()"
def test_ldconfig_calls_only_inside_geteuid_guard():
diff --git a/tests/python/test_no_torch_filtering.py b/tests/python/test_no_torch_filtering.py
index 29cadf87ae..f7a90fc6a8 100644
--- a/tests/python/test_no_torch_filtering.py
+++ b/tests/python/test_no_torch_filtering.py
@@ -156,9 +156,7 @@ class TestFilterRequirements:
)
# First filter Windows packages, then NO_TORCH packages
intermediate = ips._filter_requirements(req, ips.WINDOWS_SKIP_PACKAGES)
- result = ips._filter_requirements(
- Path(intermediate), ips.NO_TORCH_SKIP_PACKAGES
- )
+ result = ips._filter_requirements(Path(intermediate), ips.NO_TORCH_SKIP_PACKAGES)
lines = Path(result).read_text(encoding = "utf-8").splitlines()
non_blank = [l.strip() for l in lines if l.strip()]
assert non_blank == [
@@ -177,9 +175,7 @@ class TestFilterRequirements:
result = ips._filter_requirements(req, ips.NO_TORCH_SKIP_PACKAGES)
lines = Path(result).read_text(encoding = "utf-8").splitlines()
non_blank = [l.strip() for l in lines if l.strip()]
- assert non_blank == [
- "numpy"
- ], f"VCS URL line should be filtered, got: {non_blank}"
+ assert non_blank == ["numpy"], f"VCS URL line should be filtered, got: {non_blank}"
def test_env_marker_line_filtered(self, tmp_path):
"""Package lines with env markers are still filtered by prefix."""
@@ -193,9 +189,7 @@ class TestFilterRequirements:
result = ips._filter_requirements(req, ips.NO_TORCH_SKIP_PACKAGES)
lines = Path(result).read_text(encoding = "utf-8").splitlines()
non_blank = [l.strip() for l in lines if l.strip()]
- assert non_blank == [
- "numpy"
- ], f"Env marker line should be filtered, got: {non_blank}"
+ assert non_blank == ["numpy"], f"Env marker line should be filtered, got: {non_blank}"
def test_git_plus_url_not_over_matched(self, tmp_path):
"""A git+ URL whose path contains a skip package name but does NOT start with it."""
@@ -247,9 +241,7 @@ class TestRealRequirementsFiltering:
expected = [
l
for l in original
- if not any(
- l.strip().lower().startswith(p) for p in ips.NO_TORCH_SKIP_PACKAGES
- )
+ if not any(l.strip().lower().startswith(p) for p in ips.NO_TORCH_SKIP_PACKAGES)
]
assert filtered == expected, (
f"Filtered extras.txt should match expected.\n"
@@ -259,9 +251,7 @@ class TestRealRequirementsFiltering:
def test_extras_no_deps_txt_torchcodec_and_dlpack_removed(self):
"""extras-no-deps.txt: torchcodec and torch-c-dlpack-ext must be removed."""
- result = ips._filter_requirements(
- EXTRAS_NO_DEPS_TXT, ips.NO_TORCH_SKIP_PACKAGES
- )
+ result = ips._filter_requirements(EXTRAS_NO_DEPS_TXT, ips.NO_TORCH_SKIP_PACKAGES)
filtered = self._non_blank_non_comment(Path(result))
original = self._non_blank_non_comment(EXTRAS_NO_DEPS_TXT)
@@ -273,9 +263,7 @@ class TestRealRequirementsFiltering:
expected = [
l
for l in original
- if not any(
- l.strip().lower().startswith(p) for p in ips.NO_TORCH_SKIP_PACKAGES
- )
+ if not any(l.strip().lower().startswith(p) for p in ips.NO_TORCH_SKIP_PACKAGES)
]
assert filtered == expected
@@ -291,9 +279,7 @@ class TestRealRequirementsFiltering:
def test_extras_no_deps_txt_trl_preserved(self):
"""trl should survive NO_TORCH filtering in extras-no-deps.txt."""
- result = ips._filter_requirements(
- EXTRAS_NO_DEPS_TXT, ips.NO_TORCH_SKIP_PACKAGES
- )
+ result = ips._filter_requirements(EXTRAS_NO_DEPS_TXT, ips.NO_TORCH_SKIP_PACKAGES)
filtered_text = Path(result).read_text(encoding = "utf-8").lower()
assert "trl" in filtered_text, "trl should survive NO_TORCH filtering"
@@ -370,7 +356,6 @@ class TestIsMacosConstant:
def test_is_macos_matches_platform(self):
import sys
-
expected = sys.platform == "darwin"
assert ips.IS_MACOS is expected
@@ -405,9 +390,7 @@ class TestInstallPythonStackSubprocessMock:
captured_cmds: list[list[str]] = []
def mock_run(cmd, **kw):
- captured_cmds.append(
- list(cmd) if isinstance(cmd, (list, tuple)) else [str(cmd)]
- )
+ captured_cmds.append(list(cmd) if isinstance(cmd, (list, tuple)) else [str(cmd)])
return subprocess.CompletedProcess(cmd, 0, b"", b"")
env = {"SKIP_STUDIO_BASE": "1"} if skip_base else {}
@@ -424,9 +407,7 @@ class TestInstallPythonStackSubprocessMock:
mock.patch.object(ips, "_has_rocm_gpu", return_value = False),
mock.patch("subprocess.run", side_effect = mock_run),
mock.patch.object(ips, "_bootstrap_uv", return_value = True),
- mock.patch.object(
- ips, "LOCAL_DD_UNSTRUCTURED_PLUGIN", Path("/fake/plugin")
- ),
+ mock.patch.object(ips, "LOCAL_DD_UNSTRUCTURED_PLUGIN", Path("/fake/plugin")),
mock.patch("pathlib.Path.is_dir", return_value = True),
mock.patch("pathlib.Path.is_file", return_value = True),
):
@@ -469,9 +450,7 @@ class TestInstallPythonStackSubprocessMock:
has_extras_nd = self._cmds_contain_file(cmds, "extras-no-deps.txt") or any(
"-r" in cmd and "tmp" in cmd.lower() for cmd in cmds
)
- assert (
- has_extras_nd
- ), "extras-no-deps.txt (or its filtered temp) should be called"
+ assert has_extras_nd, "extras-no-deps.txt (or its filtered temp) should be called"
# -- IS_WINDOWS=True + NO_TORCH=True (stacked) --
@@ -570,17 +549,13 @@ class TestOverridesSkip:
def test_no_torch_guard_exists_in_source(self):
"""The install_python_stack source must contain a NO_TORCH guard around overrides."""
source = Path(ips.__file__).read_text(encoding = "utf-8")
- assert (
- "if NO_TORCH:" in source
- ), "NO_TORCH guard not found in install_python_stack.py"
+ assert "if NO_TORCH:" in source, "NO_TORCH guard not found in install_python_stack.py"
def test_overrides_skipped_when_no_torch(self):
"""With NO_TORCH=True on the module, pip_install should NOT be called for overrides."""
source = Path(ips.__file__).read_text(encoding = "utf-8")
overrides_match = re.search(r"if NO_TORCH:.*?overrides", source, re.DOTALL)
- assert (
- overrides_match is not None
- ), "Expected NO_TORCH conditional before overrides install"
+ assert overrides_match is not None, "Expected NO_TORCH conditional before overrides install"
# ── install.sh --no-torch flag tests ──────────────────────────────────
@@ -599,33 +574,21 @@ class TestInstallShNoTorchFlag:
def test_no_torch_flag_in_case_statement(self):
"""--no-torch must appear in the flag parser case statement."""
- assert (
- "--no-torch)" in self.source
- ), "--no-torch not found in install.sh flag parser"
+ assert "--no-torch)" in self.source, "--no-torch not found in install.sh flag parser"
def test_no_torch_flag_variable_initialized(self):
"""_NO_TORCH_FLAG must be initialized to false."""
- assert (
- "_NO_TORCH_FLAG=false" in self.source
- ), "_NO_TORCH_FLAG=false not found in install.sh"
+ assert "_NO_TORCH_FLAG=false" in self.source, "_NO_TORCH_FLAG=false not found in install.sh"
def test_skip_torch_variable_exists(self):
"""SKIP_TORCH variable must be defined."""
- assert (
- "SKIP_TORCH=false" in self.source
- ), "SKIP_TORCH=false not found in install.sh"
- assert (
- "SKIP_TORCH=true" in self.source
- ), "SKIP_TORCH=true not found in install.sh"
+ assert "SKIP_TORCH=false" in self.source, "SKIP_TORCH=false not found in install.sh"
+ assert "SKIP_TORCH=true" in self.source, "SKIP_TORCH=true not found in install.sh"
def test_skip_torch_driven_by_flag_and_mac_intel(self):
"""SKIP_TORCH must check both _NO_TORCH_FLAG and MAC_INTEL."""
- assert (
- "_NO_TORCH_FLAG" in self.source
- ), "_NO_TORCH_FLAG not referenced in SKIP_TORCH logic"
- assert (
- "MAC_INTEL" in self.source
- ), "MAC_INTEL not referenced in SKIP_TORCH logic"
+ assert "_NO_TORCH_FLAG" in self.source, "_NO_TORCH_FLAG not referenced in SKIP_TORCH logic"
+ assert "MAC_INTEL" in self.source, "MAC_INTEL not referenced in SKIP_TORCH logic"
def test_unsloth_no_torch_uses_skip_torch(self):
"""UNSLOTH_NO_TORCH must reference $SKIP_TORCH, not $MAC_INTEL."""
@@ -633,18 +596,12 @@ class TestInstallShNoTorchFlag:
matches = re.findall(r'UNSLOTH_NO_TORCH="\$(\w+)"', self.source)
for var in matches:
- assert (
- var == "SKIP_TORCH"
- ), f"UNSLOTH_NO_TORCH references ${var} instead of $SKIP_TORCH"
+ assert var == "SKIP_TORCH", f"UNSLOTH_NO_TORCH references ${var} instead of $SKIP_TORCH"
def test_cpu_hint_message_exists(self):
"""CPU hint message must exist in install.sh."""
- assert (
- "No GPU detected" in self.source
- ), "CPU hint message not found in install.sh"
- assert (
- "--no-torch" in self.source
- ), "--no-torch suggestion not found in CPU hint"
+ assert "No GPU detected" in self.source, "CPU hint message not found in install.sh"
+ assert "--no-torch" in self.source, "--no-torch suggestion not found in CPU hint"
def test_no_torch_flag_parsing_subprocess(self):
"""--no-torch flag sets _NO_TORCH_FLAG=true (subprocess test)."""
diff --git a/tests/python/test_orpo_processor_text_tokenizer.py b/tests/python/test_orpo_processor_text_tokenizer.py
index 44c4e26a86..9ae205c6b9 100644
--- a/tests/python/test_orpo_processor_text_tokenizer.py
+++ b/tests/python/test_orpo_processor_text_tokenizer.py
@@ -34,7 +34,12 @@ class _Tokenizer:
def __init__(self):
self.calls = []
- def __call__(self, text, add_special_tokens = False, **kwargs):
+ def __call__(
+ self,
+ text,
+ add_special_tokens = False,
+ **kwargs,
+ ):
self.calls.append((text, add_special_tokens, kwargs))
ids = [ord(c) % 31 + 3 for c in text]
return {"input_ids": ids, "attention_mask": [1] * len(ids)}
@@ -60,7 +65,11 @@ class _Trainer:
self.padding_value = 0
-def _exec_rewritten(function_name, source, extra_ns = None):
+def _exec_rewritten(
+ function_name,
+ source,
+ extra_ns = None,
+):
rewriter = _load_orpo_rewriter()
rewritten = rewriter(function_name, source)
ns = {} if extra_ns is None else dict(extra_ns)
diff --git a/tests/python/test_studio_import_no_torch.py b/tests/python/test_studio_import_no_torch.py
index 5592a282ff..c8b67023f5 100644
--- a/tests/python/test_studio_import_no_torch.py
+++ b/tests/python/test_studio_import_no_torch.py
@@ -23,15 +23,9 @@ from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[2]
-DATA_COLLATORS = (
- REPO_ROOT / "studio" / "backend" / "utils" / "datasets" / "data_collators.py"
-)
-CHAT_TEMPLATES = (
- REPO_ROOT / "studio" / "backend" / "utils" / "datasets" / "chat_templates.py"
-)
-FORMAT_CONVERSION = (
- REPO_ROOT / "studio" / "backend" / "utils" / "datasets" / "format_conversion.py"
-)
+DATA_COLLATORS = REPO_ROOT / "studio" / "backend" / "utils" / "datasets" / "data_collators.py"
+CHAT_TEMPLATES = REPO_ROOT / "studio" / "backend" / "utils" / "datasets" / "chat_templates.py"
+FORMAT_CONVERSION = REPO_ROOT / "studio" / "backend" / "utils" / "datasets" / "format_conversion.py"
def _has_uv() -> bool:
@@ -72,9 +66,7 @@ def no_torch_venv(request, tmp_path_factory):
[str(venv_python), "-c", "import torch"],
capture_output = True,
)
- assert (
- check.returncode != 0
- ), f"torch should NOT be importable in fresh {py_version} venv"
+ assert check.returncode != 0, f"torch should NOT be importable in fresh {py_version} venv"
return str(venv_python)
@@ -223,9 +215,7 @@ class TestDataCollatorsNoTorchVenv:
capture_output = True,
timeout = 30,
)
- assert (
- result.returncode == 0
- ), f"DeepSeekOCRDataCollator failed:\n{result.stderr.decode()}"
+ assert result.returncode == 0, f"DeepSeekOCRDataCollator failed:\n{result.stderr.decode()}"
assert b"OK: DeepSeekOCRDataCollator instantiated" in result.stdout
def test_dataclass_vlm_collator_instantiable(self, no_torch_venv):
@@ -246,9 +236,7 @@ class TestDataCollatorsNoTorchVenv:
capture_output = True,
timeout = 30,
)
- assert (
- result.returncode == 0
- ), f"VLMDataCollator failed:\n{result.stderr.decode()}"
+ assert result.returncode == 0, f"VLMDataCollator failed:\n{result.stderr.decode()}"
assert b"OK: VLMDataCollator instantiated" in result.stdout
@@ -529,12 +517,9 @@ class TestNegativeControls:
capture_output = True,
timeout = 30,
)
+ assert result.returncode != 0, "Expected failure when 'import torch' is prepended"
assert (
- result.returncode != 0
- ), "Expected failure when 'import torch' is prepended"
- assert (
- b"ModuleNotFoundError" in result.stderr
- or b"ImportError" in result.stderr
+ b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
), f"Expected ImportError, got:\n{result.stderr.decode()}"
finally:
os.unlink(temp_file)
@@ -577,6 +562,4 @@ class TestNegativeControls:
timeout = 30,
)
assert result.returncode != 0, "import torch should fail in no-torch venv"
- assert (
- b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
- )
+ assert b"ModuleNotFoundError" in result.stderr or b"ImportError" in result.stderr
diff --git a/tests/python/test_tokenizers_and_torch_constraint.py b/tests/python/test_tokenizers_and_torch_constraint.py
index 4be53d2d03..17f2d17f94 100644
--- a/tests/python/test_tokenizers_and_torch_constraint.py
+++ b/tests/python/test_tokenizers_and_torch_constraint.py
@@ -18,9 +18,7 @@ _TESTS_DIR = pathlib.Path(__file__).resolve().parent.parent # tests/
_REPO_ROOT = _TESTS_DIR.parent # unsloth/
_INSTALL_SH = _REPO_ROOT / "install.sh"
_INSTALL_PS1 = _REPO_ROOT / "install.ps1"
-_NO_TORCH_RT = (
- _REPO_ROOT / "studio" / "backend" / "requirements" / "no-torch-runtime.txt"
-)
+_NO_TORCH_RT = _REPO_ROOT / "studio" / "backend" / "requirements" / "no-torch-runtime.txt"
def _read(path: pathlib.Path) -> str:
@@ -45,30 +43,23 @@ class TestStructuralTokenizers:
def test_tokenizers_present(self):
"""tokenizers must be a standalone package line."""
pkgs = _lines(_NO_TORCH_RT)
- bare_names = [
- p.split(">")[0].split("<")[0].split("!")[0].split("=")[0] for p in pkgs
- ]
+ bare_names = [p.split(">")[0].split("<")[0].split("!")[0].split("=")[0] for p in pkgs]
assert "tokenizers" in bare_names
def test_tokenizers_before_transformers(self):
"""tokenizers should appear before transformers (install order intent)."""
pkgs = _lines(_NO_TORCH_RT)
- bare_names = [
- p.split(">")[0].split("<")[0].split("!")[0].split("=")[0] for p in pkgs
- ]
+ bare_names = [p.split(">")[0].split("<")[0].split("!")[0].split("=")[0] for p in pkgs]
idx_tok = bare_names.index("tokenizers")
idx_tf = bare_names.index("transformers")
assert idx_tok < idx_tf, (
- f"tokenizers at index {idx_tok} should appear before "
- f"transformers at index {idx_tf}"
+ f"tokenizers at index {idx_tok} should appear before " f"transformers at index {idx_tf}"
)
def test_torch_not_in_no_torch_file(self):
"""torch itself must NOT be listed in the no-torch requirements."""
pkgs = _lines(_NO_TORCH_RT)
- bare_names = [
- p.split(">")[0].split("<")[0].split("!")[0].split("=")[0] for p in pkgs
- ]
+ bare_names = [p.split(">")[0].split("<")[0].split("!")[0].split("=")[0] for p in pkgs]
assert "torch" not in bare_names
@@ -409,9 +400,7 @@ class TestE2ETokenizersFix:
r = self._pip_install(venv, "--no-deps", "-r", str(_NO_TORCH_RT))
assert r.returncode == 0, f"Install failed: {r.stderr}"
- result = self._run_python(
- venv, "from transformers import AutoConfig; print('OK')"
- )
+ result = self._run_python(venv, "from transformers import AutoConfig; print('OK')")
assert (
result.returncode == 0
), f"AutoConfig import failed:\nstdout: {result.stdout}\nstderr: {result.stderr}"
@@ -441,22 +430,15 @@ class TestE2ETokenizersFix:
req_no_tokenizers = tmp_path / "no-tokenizers.txt"
req_no_tokenizers.write_text(
"\n".join(
- line
- for line in _read(_NO_TORCH_RT).splitlines()
- if line.strip() != "tokenizers"
+ line for line in _read(_NO_TORCH_RT).splitlines() if line.strip() != "tokenizers"
),
encoding = "utf-8",
)
r = self._pip_install(venv, "--no-deps", "-r", str(req_no_tokenizers))
assert r.returncode == 0, f"Install failed: {r.stderr}"
result = self._run_python(venv, "from transformers import AutoConfig")
- assert (
- result.returncode != 0
- ), "AutoConfig should fail without tokenizers installed"
- assert (
- "tokenizers" in result.stderr.lower()
- or "ModuleNotFoundError" in result.stderr
- )
+ assert result.returncode != 0, "AutoConfig should fail without tokenizers installed"
+ assert "tokenizers" in result.stderr.lower() or "ModuleNotFoundError" in result.stderr
# ======================================================================
@@ -535,9 +517,7 @@ class TestE2EFullNoTorchSandbox:
venv = self._create_venv(tmp_path, "full-no-torch")
r = self._pip_install(venv, "--no-deps", "-r", str(_NO_TORCH_RT))
assert r.returncode == 0, f"Install failed: {r.stderr}"
- result = self._run_python(
- venv, "from transformers import AutoConfig; print('OK')"
- )
+ result = self._run_python(venv, "from transformers import AutoConfig; print('OK')")
assert (
result.returncode == 0
), f"AutoConfig failed:\nstdout: {result.stdout}\nstderr: {result.stderr}"
diff --git a/tests/python/test_unsloth_run_tool_policy_resolver.py b/tests/python/test_unsloth_run_tool_policy_resolver.py
index 6aff02494b..6e3e3a722d 100644
--- a/tests/python/test_unsloth_run_tool_policy_resolver.py
+++ b/tests/python/test_unsloth_run_tool_policy_resolver.py
@@ -141,15 +141,11 @@ class TestZeroHost:
class TestIsExternalHost:
- @pytest.mark.parametrize(
- "host", ["127.0.0.1", "localhost", "::1", "LOCALHOST", "Localhost"]
- )
+ @pytest.mark.parametrize("host", ["127.0.0.1", "localhost", "::1", "LOCALHOST", "Localhost"])
def test_loopback_aliases_are_local(self, host):
assert is_external_host(host) is False
- @pytest.mark.parametrize(
- "host", ["0.0.0.0", "::", "192.168.1.5", "10.0.0.1", "example.com"]
- )
+ @pytest.mark.parametrize("host", ["0.0.0.0", "::", "192.168.1.5", "10.0.0.1", "example.com"])
def test_non_loopback_is_external(self, host):
assert is_external_host(host) is True
diff --git a/tests/qlora/test_hf_qlora_train_and_merge.py b/tests/qlora/test_hf_qlora_train_and_merge.py
index ae975b0266..0892627c46 100644
--- a/tests/qlora/test_hf_qlora_train_and_merge.py
+++ b/tests/qlora/test_hf_qlora_train_and_merge.py
@@ -91,9 +91,7 @@ if __name__ == "__main__":
print(training_args)
print(peft_config)
- trainer = setup_trainer(
- model, tokenizer, dataset, training_args, peft_config = peft_config
- )
+ trainer = setup_trainer(model, tokenizer, dataset, training_args, peft_config = peft_config)
with header_footer_context("Model"):
print(type(model.model))
diff --git a/tests/saving/gpt-oss-merge/test_merged_model.py b/tests/saving/gpt-oss-merge/test_merged_model.py
index 48f0ed2d3d..497c74debf 100644
--- a/tests/saving/gpt-oss-merge/test_merged_model.py
+++ b/tests/saving/gpt-oss-merge/test_merged_model.py
@@ -42,9 +42,7 @@ inputs = merged_tokenizer.apply_chat_template(
reasoning_effort = "low", # **NEW!** Set reasoning effort to low, medium or high
).to(merged_model.device)
-_ = merged_model.generate(
- **inputs, max_new_tokens = 512, streamer = TextStreamer(merged_tokenizer)
-)
+_ = merged_model.generate(**inputs, max_new_tokens = 512, streamer = TextStreamer(merged_tokenizer))
print("\n✅ Inference complete.")
# --- Final Cleanup ---
@@ -54,7 +52,5 @@ torch.cuda.empty_cache()
gc.collect()
safe_remove_directory("./gpt-oss-finetuned-merged")
-safe_remove_directory(
- "./unsloth_compiled_cache"
-) # Clean up cache created by this process
+safe_remove_directory("./unsloth_compiled_cache") # Clean up cache created by this process
print("✅ Final cleanup complete. Exiting inference script.")
diff --git a/tests/saving/gpt-oss-merge/train_and_merge.py b/tests/saving/gpt-oss-merge/train_and_merge.py
index 308d19bfb4..8c76ff9662 100644
--- a/tests/saving/gpt-oss-merge/train_and_merge.py
+++ b/tests/saving/gpt-oss-merge/train_and_merge.py
@@ -28,9 +28,7 @@ tokenizer = None
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
@@ -84,9 +82,7 @@ print("Fine-tuning complete.")
# --- Merge and Save ---
print("\n💾 Merging and saving the 16-bit model to './gpt-oss-finetuned-merged'...")
-model.save_pretrained_merged(
- save_directory = "./gpt-oss-finetuned-merged", tokenizer = tokenizer
-)
+model.save_pretrained_merged(save_directory = "./gpt-oss-finetuned-merged", tokenizer = tokenizer)
print("✅ Model merged and saved.")
# --- Cleanup ---
@@ -96,7 +92,5 @@ torch.cuda.empty_cache()
gc.collect()
safe_remove_directory("./outputs")
-safe_remove_directory(
- "./unsloth_compiled_cache"
-) # Clean up the cache created by this process
+safe_remove_directory("./unsloth_compiled_cache") # Clean up the cache created by this process
print("✅ Cleanup complete. Exiting training script.")
diff --git a/tests/saving/language_models/test_merge_4bit_validation.py b/tests/saving/language_models/test_merge_4bit_validation.py
index 343e737710..c889001706 100644
--- a/tests/saving/language_models/test_merge_4bit_validation.py
+++ b/tests/saving/language_models/test_merge_4bit_validation.py
@@ -16,9 +16,7 @@ from tests.utils.cleanup_utils import safe_remove_directory
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
@@ -51,9 +49,7 @@ tokenizer = get_chat_template(
)
# Load small dataset for quick training
-dataset_train = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "train[:100]"
-)
+dataset_train = load_dataset("allenai/openassistant-guanaco-reformatted", split = "train[:100]")
dataset_train = dataset_train.map(formatting_prompts_func, batched = True)
print("✅ Base model loaded successfully!")
diff --git a/tests/saving/language_models/test_merge_model_perplexity_llama-3.2.py b/tests/saving/language_models/test_merge_model_perplexity_llama-3.2.py
index dd0e8c25c6..3f2b811b07 100644
--- a/tests/saving/language_models/test_merge_model_perplexity_llama-3.2.py
+++ b/tests/saving/language_models/test_merge_model_perplexity_llama-3.2.py
@@ -35,15 +35,17 @@ from tests.utils.perplexity_eval import (
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
-def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit = False):
+def load_and_compute_8bit_ppl(
+ result_queue,
+ load_in_4bit = False,
+ load_in_8bit = False,
+):
"""Load model and compute perplexity in subprocess"""
from unsloth import FastLanguageModel
from unsloth.chat_templates import get_chat_template
@@ -63,17 +65,13 @@ def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit =
)
# Load dataset fresh in subprocess
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
# Format the dataset
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- merged_tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ merged_tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
@@ -130,12 +128,8 @@ if __name__ == "__main__":
from unsloth.chat_templates import standardize_sharegpt
- dataset_train = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "train"
- )
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_train = load_dataset("allenai/openassistant-guanaco-reformatted", split = "train")
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
dataset_train = dataset_train.map(formatting_prompts_func, batched = True)
dataset_ppl = dataset_ppl.map(formatting_prompts_func, batched = True)
diff --git a/tests/saving/language_models/test_merge_model_perplexity_mistral.py b/tests/saving/language_models/test_merge_model_perplexity_mistral.py
index 14e657c68a..d17a20d755 100644
--- a/tests/saving/language_models/test_merge_model_perplexity_mistral.py
+++ b/tests/saving/language_models/test_merge_model_perplexity_mistral.py
@@ -30,7 +30,11 @@ from tests.utils.perplexity_eval import (
)
-def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit = False):
+def load_and_compute_8bit_ppl(
+ result_queue,
+ load_in_4bit = False,
+ load_in_8bit = False,
+):
"""Load model and compute perplexity in subprocess"""
from unsloth import FastLanguageModel
from tests.utils.perplexity_eval import ppl_model
@@ -49,9 +53,7 @@ def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit =
# )
# Load dataset fresh in subprocess
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
alpaca_prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
@@ -90,10 +92,7 @@ def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit =
outputs.append(assistant_message)
# Create formatted text
- text = (
- alpaca_prompt.format(instruction, user_message, assistant_message)
- + EOS_TOKEN
- )
+ text = alpaca_prompt.format(instruction, user_message, assistant_message) + EOS_TOKEN
texts.append(text)
return {
@@ -186,10 +185,7 @@ if __name__ == "__main__":
outputs.append(assistant_message)
# Create formatted text
- text = (
- alpaca_prompt.format(instruction, user_message, assistant_message)
- + EOS_TOKEN
- )
+ text = alpaca_prompt.format(instruction, user_message, assistant_message) + EOS_TOKEN
texts.append(text)
return {
@@ -199,12 +195,8 @@ if __name__ == "__main__":
"text": texts,
}
- dataset_train = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "train"
- )
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_train = load_dataset("allenai/openassistant-guanaco-reformatted", split = "train")
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
dataset_train = dataset_train.map(formatting_prompts_func, batched = True)
dataset_ppl = dataset_ppl.map(formatting_prompts_func, batched = True)
diff --git a/tests/saving/language_models/test_merge_model_perplexity_phi_4.py b/tests/saving/language_models/test_merge_model_perplexity_phi_4.py
index bebea8168e..6dbdf36032 100644
--- a/tests/saving/language_models/test_merge_model_perplexity_phi_4.py
+++ b/tests/saving/language_models/test_merge_model_perplexity_phi_4.py
@@ -35,9 +35,7 @@ from tests.utils.perplexity_eval import (
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {
@@ -45,7 +43,11 @@ def formatting_prompts_func(examples):
}
-def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit = False):
+def load_and_compute_8bit_ppl(
+ result_queue,
+ load_in_4bit = False,
+ load_in_8bit = False,
+):
"""Load model and compute perplexity in subprocess"""
from unsloth import FastLanguageModel
from unsloth.chat_templates import get_chat_template
@@ -65,17 +67,13 @@ def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit =
)
# Load dataset fresh in subprocess
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
# Format the dataset
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- merged_tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ merged_tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
@@ -130,12 +128,8 @@ if __name__ == "__main__":
chat_template = "phi-4",
)
- dataset_train = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "train"
- )
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_train = load_dataset("allenai/openassistant-guanaco-reformatted", split = "train")
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
dataset_train = dataset_train.map(formatting_prompts_func, batched = True)
dataset_ppl = dataset_ppl.map(formatting_prompts_func, batched = True)
diff --git a/tests/saving/language_models/test_merged_model_perplexity_llama-3.1-8b.py b/tests/saving/language_models/test_merged_model_perplexity_llama-3.1-8b.py
index c6da9e2ca6..a0624f0c2c 100644
--- a/tests/saving/language_models/test_merged_model_perplexity_llama-3.1-8b.py
+++ b/tests/saving/language_models/test_merged_model_perplexity_llama-3.1-8b.py
@@ -34,15 +34,17 @@ from tests.utils.perplexity_eval import (
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
-def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit = False):
+def load_and_compute_8bit_ppl(
+ result_queue,
+ load_in_4bit = False,
+ load_in_8bit = False,
+):
"""Load model and compute perplexity in subprocess"""
from unsloth import FastLanguageModel
from unsloth.chat_templates import get_chat_template
@@ -62,17 +64,13 @@ def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit =
)
# Load dataset fresh in subprocess
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
# Format the dataset
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- merged_tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ merged_tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
@@ -129,12 +127,8 @@ if __name__ == "__main__":
from unsloth.chat_templates import standardize_sharegpt
- dataset_train = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "train"
- )
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_train = load_dataset("allenai/openassistant-guanaco-reformatted", split = "train")
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
dataset_train = dataset_train.map(formatting_prompts_func, batched = True)
dataset_ppl = dataset_ppl.map(formatting_prompts_func, batched = True)
diff --git a/tests/saving/language_models/test_merged_model_perplexity_qwen_2.5.py b/tests/saving/language_models/test_merged_model_perplexity_qwen_2.5.py
index d63bb9fe09..0b377eca81 100644
--- a/tests/saving/language_models/test_merged_model_perplexity_qwen_2.5.py
+++ b/tests/saving/language_models/test_merged_model_perplexity_qwen_2.5.py
@@ -78,7 +78,11 @@ def formatting_prompts_func(examples):
}
-def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit = False):
+def load_and_compute_8bit_ppl(
+ result_queue,
+ load_in_4bit = False,
+ load_in_8bit = False,
+):
"""Load model and compute perplexity in subprocess"""
from unsloth import FastLanguageModel
from tests.utils.perplexity_eval import ppl_model
@@ -97,9 +101,7 @@ def load_and_compute_8bit_ppl(result_queue, load_in_4bit = False, load_in_8bit =
# )
# Load dataset fresh in subprocess
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
alpaca_prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
@@ -191,12 +193,8 @@ if __name__ == "__main__":
attn_implementation = attn_implementation,
)
- dataset_train = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "train"
- )
- dataset_ppl = load_dataset(
- "allenai/openassistant-guanaco-reformatted", split = "eval"
- )
+ dataset_train = load_dataset("allenai/openassistant-guanaco-reformatted", split = "train")
+ dataset_ppl = load_dataset("allenai/openassistant-guanaco-reformatted", split = "eval")
dataset_train = dataset_train.map(formatting_prompts_func, batched = True)
dataset_ppl = dataset_ppl.map(formatting_prompts_func, batched = True)
diff --git a/tests/saving/language_models/test_push_to_hub_merged.py b/tests/saving/language_models/test_push_to_hub_merged.py
index 58d589305a..aa79394556 100644
--- a/tests/saving/language_models/test_push_to_hub_merged.py
+++ b/tests/saving/language_models/test_push_to_hub_merged.py
@@ -36,9 +36,7 @@ from tests.utils.perplexity_eval import (
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
@@ -176,9 +174,7 @@ try:
print("=== TESTING MODEL DOWNLOAD ===".center(80))
print("=" * 80 + "\n")
# Force download even if cached
- model, tokenizer = FastLanguageModel.from_pretrained(
- f"{hf_username}/merged_llama_text_model"
- )
+ model, tokenizer = FastLanguageModel.from_pretrained(f"{hf_username}/merged_llama_text_model")
success["download"] = True
print("✅ Model downloaded successfully!")
except Exception as e:
diff --git a/tests/saving/language_models/test_push_to_hub_merged_sharded_index_file.py b/tests/saving/language_models/test_push_to_hub_merged_sharded_index_file.py
index 038565d170..38b82c5469 100644
--- a/tests/saving/language_models/test_push_to_hub_merged_sharded_index_file.py
+++ b/tests/saving/language_models/test_push_to_hub_merged_sharded_index_file.py
@@ -36,9 +36,7 @@ from tests.utils.perplexity_eval import (
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [
- tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {"text": texts}
@@ -195,9 +193,7 @@ try:
print("=== TESTING MODEL DOWNLOAD ===".center(80))
print("=" * 80 + "\n")
# Force download even if cached
- model, tokenizer = FastLanguageModel.from_pretrained(
- f"{hf_username}/merged_llama_text_model"
- )
+ model, tokenizer = FastLanguageModel.from_pretrained(f"{hf_username}/merged_llama_text_model")
success["download"] = True
print("✅ Model downloaded successfully!")
except Exception as e:
diff --git a/tests/saving/language_models/test_save_merged_grpo_model.py b/tests/saving/language_models/test_save_merged_grpo_model.py
index 67b649305a..b5d8025fcb 100644
--- a/tests/saving/language_models/test_save_merged_grpo_model.py
+++ b/tests/saving/language_models/test_save_merged_grpo_model.py
@@ -24,7 +24,11 @@ max_seq_length = 2048 # Can increase for longer reasoning traces
lora_rank = 64 # Larger rank = smarter, but slower
-def evaluate_merged_model(result_queue, load_in_4bit = False, load_in_8bit = False):
+def evaluate_merged_model(
+ result_queue,
+ load_in_4bit = False,
+ load_in_8bit = False,
+):
from unsloth import FastLanguageModel
from tests.utils.aime_eval import evaluate_model_aime
@@ -176,12 +180,14 @@ def training_run(result_queue):
avg_length = sum(lengths) / len(lengths)
min_length = min(lengths)
- print(
- f"Prompt lengths - Min: {min_length}, Max: {max_length}, Avg: {avg_length:.1f}"
- )
+ print(f"Prompt lengths - Min: {min_length}, Max: {max_length}, Avg: {avg_length:.1f}")
return max_length, avg_length
- def extract_unsloth_answer(text, start_tag = "", end_tag = ""):
+ def extract_unsloth_answer(
+ text,
+ start_tag = "",
+ end_tag = "",
+ ):
"""Extract answer from Unsloth SOLUTION tags"""
pattern = re.escape(start_tag) + r"(.*?)" + re.escape(end_tag)
matches = re.findall(pattern, text, re.DOTALL)
@@ -265,9 +271,7 @@ def training_run(result_queue):
ground_truth_num = float(norm_ground_truth)
if ground_truth_num != 0:
- relative_error = abs(extracted_num - ground_truth_num) / abs(
- ground_truth_num
- )
+ relative_error = abs(extracted_num - ground_truth_num) / abs(ground_truth_num)
if relative_error < 0.01:
return True, True, 0.9
@@ -302,10 +306,7 @@ def training_run(result_queue):
)
responses = [completion[0]["content"] for completion in completions]
- rewards = [
- 3.0 if re.match(pattern, response, re.DOTALL) else 0.0
- for response in responses
- ]
+ rewards = [3.0 if re.match(pattern, response, re.DOTALL) else 0.0 for response in responses]
return rewards
def match_format_approximately(completions, **kwargs):
@@ -405,9 +406,7 @@ def training_run(result_queue):
format_improvement = (
result["correct_format_pct"] - base_result["correct_format_pct"]
)
- exact_improvement = (
- result["exact_match_pct"] - base_result["exact_match_pct"]
- )
+ exact_improvement = result["exact_match_pct"] - base_result["exact_match_pct"]
plausible_improvement = (
result["plausible_match_pct"] - base_result["plausible_match_pct"]
)
@@ -440,9 +439,7 @@ def training_run(result_queue):
if torch.cuda.is_available():
allocated = torch.cuda.memory_allocated() / 1024**3
reserved = torch.cuda.memory_reserved() / 1024**3
- print(
- f"GPU memory - Allocated: {allocated:.2f} GB, Reserved: {reserved:.2f} GB"
- )
+ print(f"GPU memory - Allocated: {allocated:.2f} GB, Reserved: {reserved:.2f} GB")
"""#### Data Loading and Preparation"""
@@ -486,9 +483,7 @@ def training_run(result_queue):
def formatting_prompts_func(examples):
convos = examples["prompt"]
texts = [
- tokenizer.apply_chat_template(
- convo, tokenize = False, add_generation_prompt = False
- )
+ tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False)
for convo in convos
]
return {
@@ -715,9 +710,7 @@ def training_run(result_queue):
# Save as merged model
try:
- model.save_pretrained_merged(
- "final_merged_model", tokenizer, save_method = "merged_16bit"
- )
+ model.save_pretrained_merged("final_merged_model", tokenizer, save_method = "merged_16bit")
print("✅ Merged model saved to: final_merged_model/")
except Exception as e:
print(f"⚠️ Could not save merged model: {e}")
diff --git a/tests/saving/test_fix_sentencepiece_gguf_robustness.py b/tests/saving/test_fix_sentencepiece_gguf_robustness.py
index 49bd70fa2f..9c61ca4067 100644
--- a/tests/saving/test_fix_sentencepiece_gguf_robustness.py
+++ b/tests/saving/test_fix_sentencepiece_gguf_robustness.py
@@ -44,9 +44,7 @@ def test_user_defined_special_piece_is_not_retyped(tmp_path):
]
(tmp_path / "tokenizer.model").write_bytes(_build(pieces))
(tmp_path / "tokenizer.json").write_text(
- json.dumps(
- {"added_tokens": [{"id": 2, "content": "", "special": True}]}
- )
+ json.dumps({"added_tokens": [{"id": 2, "content": "", "special": True}]})
)
fix_sentencepiece_gguf(str(tmp_path))
got = dict(_read(str(tmp_path / "tokenizer.model")))
@@ -87,10 +85,7 @@ def test_save_py_except_clause_is_broad_exception():
with open(_SAVE_PY) as f:
tree = ast.parse(f.read())
for node in ast.walk(tree):
- if (
- isinstance(node, ast.FunctionDef)
- and node.name == "unsloth_save_pretrained_gguf"
- ):
+ if isinstance(node, ast.FunctionDef) and node.name == "unsloth_save_pretrained_gguf":
for subnode in ast.walk(node):
if isinstance(subnode, ast.Try):
body_src = "\n".join(ast.unparse(s) for s in subnode.body)
diff --git a/tests/saving/test_preserve_tokenizer_eos_token.py b/tests/saving/test_preserve_tokenizer_eos_token.py
index 6e2f8c7f9d..2ea40ab778 100644
--- a/tests/saving/test_preserve_tokenizer_eos_token.py
+++ b/tests/saving/test_preserve_tokenizer_eos_token.py
@@ -16,8 +16,7 @@ def _load_preserve_helper():
helper = next(
node
for node in tree.body
- if isinstance(node, ast.FunctionDef)
- and node.name == "_preserve_tokenizer_eos_token"
+ if isinstance(node, ast.FunctionDef) and node.name == "_preserve_tokenizer_eos_token"
)
module = ast.Module(body = [helper], type_ignores = [])
ast.fix_missing_locations(module)
@@ -46,9 +45,7 @@ def test_preserve_tokenizer_eos_token_supports_processor_tokenizer(tmp_path):
preserve = _load_preserve_helper()
tokenizer_config = tmp_path / "tokenizer_config.json"
tokenizer_config.write_text(json.dumps({"eos_token": ""}), encoding = "utf-8")
- processor = types.SimpleNamespace(
- tokenizer = types.SimpleNamespace(eos_token = "")
- )
+ processor = types.SimpleNamespace(tokenizer = types.SimpleNamespace(eos_token = ""))
preserve(processor, tmp_path)
diff --git a/tests/saving/test_save_shell_injection.py b/tests/saving/test_save_shell_injection.py
index c6c2c8fe15..b02748c250 100644
--- a/tests/saving/test_save_shell_injection.py
+++ b/tests/saving/test_save_shell_injection.py
@@ -19,10 +19,7 @@ def _assert_safe_ggml_calls(calls: list[ast.Call]) -> None:
popen_calls = []
for call in calls:
if isinstance(call.func, ast.Attribute) and call.func.attr == "Popen":
- if (
- isinstance(call.func.value, ast.Name)
- and call.func.value.id == "subprocess"
- ):
+ if isinstance(call.func.value, ast.Name) and call.func.value.id == "subprocess":
popen_calls.append(call)
assert popen_calls, "Expected at least one subprocess.Popen call"
@@ -54,9 +51,7 @@ def _assert_safe_ggml_calls(calls: list[ast.Call]) -> None:
assert call.args, "subprocess.Popen must receive argv as a positional argument"
argv = call.args[0]
- assert isinstance(
- argv, ast.List
- ), "subprocess.Popen must be called with an argv list"
+ assert isinstance(argv, ast.List), "subprocess.Popen must be called with an argv list"
assert len(argv.elts) == 5, "GGML conversion argv should have five elements"
second_arg = argv.elts[1]
diff --git a/tests/saving/test_unsloth_save.py b/tests/saving/test_unsloth_save.py
index 35fdad6ba0..c7dd712734 100644
--- a/tests/saving/test_unsloth_save.py
+++ b/tests/saving/test_unsloth_save.py
@@ -132,20 +132,14 @@ def test_save_merged_16bit(model, tokenizer, temp_save_dir: str):
model.config._name_or_path.replace("/", "_"),
)
- model.save_pretrained_merged(
- save_path, tokenizer = tokenizer, save_method = "merged_16bit"
- )
+ model.save_pretrained_merged(save_path, tokenizer = tokenizer, save_method = "merged_16bit")
# Check model files
assert os.path.isdir(save_path), f"Directory {save_path} does not exist."
- assert os.path.isfile(
- os.path.join(save_path, "config.json")
- ), "config.json not found."
+ assert os.path.isfile(os.path.join(save_path, "config.json")), "config.json not found."
weight_files = [
- f
- for f in os.listdir(save_path)
- if f.endswith(".bin") or f.endswith(".safetensors")
+ f for f in os.listdir(save_path) if f.endswith(".bin") or f.endswith(".safetensors")
]
assert len(weight_files) > 0, "No weight files found in the save directory."
@@ -160,9 +154,7 @@ def test_save_merged_16bit(model, tokenizer, temp_save_dir: str):
with open(config_path, "r") as f:
config = json.load(f)
- assert (
- "quantization_config" not in config
- ), "Quantization config not found in the model config."
+ assert "quantization_config" not in config, "Quantization config not found in the model config."
# Store the size of the model files
total_size = sum(os.path.getsize(os.path.join(save_path, f)) for f in weight_files)
@@ -185,20 +177,14 @@ def test_save_merged_4bit(model, tokenizer, temp_save_dir: str):
model.config._name_or_path.replace("/", "_"),
)
- model.save_pretrained_merged(
- save_path, tokenizer = tokenizer, save_method = "merged_4bit_forced"
- )
+ model.save_pretrained_merged(save_path, tokenizer = tokenizer, save_method = "merged_4bit_forced")
# Check model files
assert os.path.isdir(save_path), f"Directory {save_path} does not exist."
- assert os.path.isfile(
- os.path.join(save_path, "config.json")
- ), "config.json not found."
+ assert os.path.isfile(os.path.join(save_path, "config.json")), "config.json not found."
weight_files = [
- f
- for f in os.listdir(save_path)
- if f.endswith(".bin") or f.endswith(".safetensors")
+ f for f in os.listdir(save_path) if f.endswith(".bin") or f.endswith(".safetensors")
]
assert len(weight_files) > 0, "No weight files found in the save directory."
@@ -223,9 +209,7 @@ def test_save_merged_4bit(model, tokenizer, temp_save_dir: str):
with open(config_path, "r") as f:
config = json.load(f)
- assert (
- "quantization_config" in config
- ), "Quantization config not found in the model config."
+ assert "quantization_config" in config, "Quantization config not found in the model config."
# Test loading the model from the saved path
loaded_model, loaded_tokenizer = FastModel.from_pretrained(
@@ -257,29 +241,19 @@ def test_save_torchao(fp16_model_tokenizer, temp_save_dir: str):
)
weight_files_16bit = [
- f
- for f in os.listdir(save_path)
- if f.endswith(".bin") or f.endswith(".safetensors")
+ f for f in os.listdir(save_path) if f.endswith(".bin") or f.endswith(".safetensors")
]
- total_16bit_size = sum(
- os.path.getsize(os.path.join(save_path, f)) for f in weight_files_16bit
- )
+ total_16bit_size = sum(os.path.getsize(os.path.join(save_path, f)) for f in weight_files_16bit)
save_file_sizes["merged_16bit"][model.config._name_or_path] = total_16bit_size
torchao_save_path = save_path + "-torchao"
# Check model files
- assert os.path.isdir(
- torchao_save_path
- ), f"Directory {torchao_save_path} does not exist."
- assert os.path.isfile(
- os.path.join(torchao_save_path, "config.json")
- ), "config.json not found."
+ assert os.path.isdir(torchao_save_path), f"Directory {torchao_save_path} does not exist."
+ assert os.path.isfile(os.path.join(torchao_save_path, "config.json")), "config.json not found."
weight_files = [
- f
- for f in os.listdir(torchao_save_path)
- if f.endswith(".bin") or f.endswith(".safetensors")
+ f for f in os.listdir(torchao_save_path) if f.endswith(".bin") or f.endswith(".safetensors")
]
assert len(weight_files) > 0, "No weight files found in the save directory."
@@ -290,9 +264,7 @@ def test_save_torchao(fp16_model_tokenizer, temp_save_dir: str):
), f"{file} not found in the save directory."
# Store the size of the model files
- total_size = sum(
- os.path.getsize(os.path.join(torchao_save_path, f)) for f in weight_files
- )
+ total_size = sum(os.path.getsize(os.path.join(torchao_save_path, f)) for f in weight_files)
save_file_sizes["torchao"][model.config._name_or_path] = total_size
assert (
@@ -304,9 +276,7 @@ def test_save_torchao(fp16_model_tokenizer, temp_save_dir: str):
with open(config_path, "r") as f:
config = json.load(f)
- assert (
- "quantization_config" in config
- ), "Quantization config not found in the model config."
+ assert "quantization_config" in config, "Quantization config not found in the model config."
# Test loading the model from the saved path
# can't set `load_in_4bit` to True because the model is torchao quantized
@@ -332,9 +302,7 @@ def test_save_and_inference_torchao(fp16_model_tokenizer, temp_save_dir: str):
print(f"Testing TorchAO save and inference for: {model_name}")
- save_path = os.path.join(
- temp_save_dir, "torchao_models", model_name.replace("/", "_")
- )
+ save_path = os.path.join(temp_save_dir, "torchao_models", model_name.replace("/", "_"))
from torchao.quantization import Int8DynamicActivationInt8WeightConfig
diff --git a/tests/saving/text_to_speech_models/test_csm.py b/tests/saving/text_to_speech_models/test_csm.py
index c1a892a8d3..dd2287d1d6 100644
--- a/tests/saving/text_to_speech_models/test_csm.py
+++ b/tests/saving/text_to_speech_models/test_csm.py
@@ -134,9 +134,7 @@ import torch
output_audio_path = "csm_audio.wav"
try:
- text = (
- "We just finished fine tuning a text to speech model... and it's pretty good!"
- )
+ text = "We just finished fine tuning a text to speech model... and it's pretty good!"
speaker_id = 0
inputs = processor(f"[{speaker_id}]{text}", add_special_tokens = True).to("cuda")
audio_values = model.generate(
diff --git a/tests/saving/text_to_speech_models/test_lasa.py b/tests/saving/text_to_speech_models/test_lasa.py
index 804ff512f9..c0c4f80e0e 100644
--- a/tests/saving/text_to_speech_models/test_lasa.py
+++ b/tests/saving/text_to_speech_models/test_lasa.py
@@ -167,9 +167,7 @@ def extract_speech_ids(speech_tokens_str):
# TTS start!
with torch.inference_mode():
with torch.amp.autocast("cuda", dtype = model.dtype):
- formatted_text = (
- f"<|TEXT_UNDERSTANDING_START|>{input_text}<|TEXT_UNDERSTANDING_END|>"
- )
+ formatted_text = f"<|TEXT_UNDERSTANDING_START|>{input_text}<|TEXT_UNDERSTANDING_END|>"
# Tokenize the text
chat = [
diff --git a/tests/saving/text_to_speech_models/test_orpheus.py b/tests/saving/text_to_speech_models/test_orpheus.py
index bd8bf14979..2915749d99 100644
--- a/tests/saving/text_to_speech_models/test_orpheus.py
+++ b/tests/saving/text_to_speech_models/test_orpheus.py
@@ -152,9 +152,7 @@ for prompt in prompts_:
all_input_ids.append(input_ids)
start_token = torch.tensor([[128259]], dtype = torch.int64) # Start of human
-end_tokens = torch.tensor(
- [[128009, 128260]], dtype = torch.int64
-) # End of text, End of human
+end_tokens = torch.tensor([[128009, 128260]], dtype = torch.int64) # End of text, End of human
all_modified_input_ids = []
for input_ids in all_input_ids:
@@ -165,9 +163,7 @@ for input_ids in all_input_ids:
all_padded_tensors = []
all_attention_masks = []
-max_length = max(
- [modified_input_ids.shape[1] for modified_input_ids in all_modified_input_ids]
-)
+max_length = max([modified_input_ids.shape[1] for modified_input_ids in all_modified_input_ids])
for modified_input_ids in all_modified_input_ids:
padding = max_length - modified_input_ids.shape[1]
padded_tensor = torch.cat(
diff --git a/tests/saving/text_to_speech_models/test_whisper.py b/tests/saving/text_to_speech_models/test_whisper.py
index 55f6d98ca0..d0eeb49d17 100644
--- a/tests/saving/text_to_speech_models/test_whisper.py
+++ b/tests/saving/text_to_speech_models/test_whisper.py
@@ -181,13 +181,9 @@ expected_phrases = [
]
transcribed_lower = transcribed_text["text"].lower()
-all_phrases_found = all(
- phrase.lower() in transcribed_lower for phrase in expected_phrases
-)
+all_phrases_found = all(phrase.lower() in transcribed_lower for phrase in expected_phrases)
-assert (
- all_phrases_found
-), f"Expected phrases not found in transcription: {transcribed_text['text']}"
+assert all_phrases_found, f"Expected phrases not found in transcription: {transcribed_text['text']}"
print("✅ Transcription contains all expected phrases!")
diff --git a/tests/saving/vision_models/test_index_file_sharded_model.py b/tests/saving/vision_models/test_index_file_sharded_model.py
index 8d107463e0..79a25ec666 100644
--- a/tests/saving/vision_models/test_index_file_sharded_model.py
+++ b/tests/saving/vision_models/test_index_file_sharded_model.py
@@ -138,9 +138,7 @@ try:
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
gradient_checkpointing = True,
- gradient_checkpointing_kwargs = {
- "use_reentrant": False
- }, # use reentrant checkpointing
+ gradient_checkpointing_kwargs = {"use_reentrant": False}, # use reentrant checkpointing
max_grad_norm = 0.3, # max gradient norm based on QLoRA paper
warmup_ratio = 0.03,
# num_train_epochs = 2, # Set this instead of max_steps for full training runs
diff --git a/tests/saving/vision_models/test_push_to_hub_merged.py b/tests/saving/vision_models/test_push_to_hub_merged.py
index fb2af4b4fe..86c7b56cf2 100644
--- a/tests/saving/vision_models/test_push_to_hub_merged.py
+++ b/tests/saving/vision_models/test_push_to_hub_merged.py
@@ -139,9 +139,7 @@ try:
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
gradient_checkpointing = True,
- gradient_checkpointing_kwargs = {
- "use_reentrant": False
- }, # use reentrant checkpointing
+ gradient_checkpointing_kwargs = {"use_reentrant": False}, # use reentrant checkpointing
max_grad_norm = 0.3, # max gradient norm based on QLoRA paper
warmup_ratio = 0.03,
# num_train_epochs = 2, # Set this instead of max_steps for full training runs
diff --git a/tests/saving/vision_models/test_save_merge_qwen2.5vl32B_model_ocr_benchmark.py b/tests/saving/vision_models/test_save_merge_qwen2.5vl32B_model_ocr_benchmark.py
index 2b24bc4a32..391d7bacca 100644
--- a/tests/saving/vision_models/test_save_merge_qwen2.5vl32B_model_ocr_benchmark.py
+++ b/tests/saving/vision_models/test_save_merge_qwen2.5vl32B_model_ocr_benchmark.py
@@ -134,9 +134,7 @@ trainer = SFTTrainer(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
gradient_checkpointing = True,
- gradient_checkpointing_kwargs = {
- "use_reentrant": False
- }, # use reentrant checkpointing
+ gradient_checkpointing_kwargs = {"use_reentrant": False}, # use reentrant checkpointing
max_grad_norm = 0.3, # max gradient norm based on QLoRA paper
warmup_ratio = 0.03,
# num_train_epochs = 2, # Set this instead of max_steps for full training runs
diff --git a/tests/saving/vision_models/test_save_merge_vision_model_ocr_benchmark.py b/tests/saving/vision_models/test_save_merge_vision_model_ocr_benchmark.py
index 16914707c2..b4812cdfa8 100644
--- a/tests/saving/vision_models/test_save_merge_vision_model_ocr_benchmark.py
+++ b/tests/saving/vision_models/test_save_merge_vision_model_ocr_benchmark.py
@@ -134,9 +134,7 @@ trainer = SFTTrainer(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
gradient_checkpointing = True,
- gradient_checkpointing_kwargs = {
- "use_reentrant": False
- }, # use reentrant checkpointing
+ gradient_checkpointing_kwargs = {"use_reentrant": False}, # use reentrant checkpointing
max_grad_norm = 0.3, # max gradient norm based on QLoRA paper
warmup_ratio = 0.03,
# num_train_epochs = 2, # Set this instead of max_steps for full training runs
diff --git a/tests/security/test_lockfile_supply_chain_audit.py b/tests/security/test_lockfile_supply_chain_audit.py
index 483bb9e763..cec07aea28 100644
--- a/tests/security/test_lockfile_supply_chain_audit.py
+++ b/tests/security/test_lockfile_supply_chain_audit.py
@@ -143,7 +143,6 @@ def test_lockfile_auditor_blocked_versions_match_scanner():
comment until the next PR factors them into a shared module).
"""
from scripts import scan_npm_packages as snp
-
assert (
lsa.BLOCKED_NPM_VERSIONS == snp.BLOCKED_NPM_VERSIONS
), "auditor and scanner BLOCKED_NPM_VERSIONS tables drifted"
@@ -275,9 +274,7 @@ def test_advisory_finding_emitted_as_single_line_annotation(tmp_path):
npm_lockfiles = [FIXTURES / "clean_lockfile.json"],
cargo_lockfiles = [lockfile],
)
- warning_lines = [
- line for line in proc.stderr.splitlines() if line.startswith("::warning::")
- ]
+ warning_lines = [line for line in proc.stderr.splitlines() if line.startswith("::warning::")]
assert warning_lines, (
"expected at least one ::warning:: annotation; " f"stderr was:\n{proc.stderr}"
)
diff --git a/tests/security/test_new_install_scripts.py b/tests/security/test_new_install_scripts.py
index 32340d2536..9a73f4b0d9 100644
--- a/tests/security/test_new_install_scripts.py
+++ b/tests/security/test_new_install_scripts.py
@@ -18,7 +18,12 @@ REPO_ROOT = Path(__file__).resolve().parents[2]
SCRIPT = REPO_ROOT / "scripts" / "check_new_install_scripts.py"
-def _run(base: Path, head: Path, *, timeout: int = 30) -> subprocess.CompletedProcess:
+def _run(
+ base: Path,
+ head: Path,
+ *,
+ timeout: int = 30,
+) -> subprocess.CompletedProcess:
return subprocess.run(
[
sys.executable,
@@ -103,9 +108,7 @@ def test_new_dep_with_postinstall_exits_1(tmp_path: Path):
head_pkgs = dict(base_pkgs)
head_pkgs["node_modules/evil-postinstall"] = {
"version": "1.0.0",
- "resolved": (
- "https://registry.npmjs.org/evil-postinstall/-/evil-postinstall-1.0.0.tgz"
- ),
+ "resolved": ("https://registry.npmjs.org/evil-postinstall/-/evil-postinstall-1.0.0.tgz"),
"integrity": "sha512-fake",
"hasInstallScript": True,
}
@@ -166,8 +169,7 @@ def test_v2_v3_lockfile_format_support(tmp_path: Path):
"node_modules/v2-postinstall-dep": {
"version": "2.0.0",
"resolved": (
- "https://registry.npmjs.org/v2-postinstall-dep/-/"
- "v2-postinstall-dep-2.0.0.tgz"
+ "https://registry.npmjs.org/v2-postinstall-dep/-/v2-postinstall-dep-2.0.0.tgz"
),
"integrity": "sha512-fake",
"hasInstallScript": True,
@@ -177,8 +179,7 @@ def test_v2_v3_lockfile_format_support(tmp_path: Path):
"v2-postinstall-dep": {
"version": "2.0.0",
"resolved": (
- "https://registry.npmjs.org/v2-postinstall-dep/-/"
- "v2-postinstall-dep-2.0.0.tgz"
+ "https://registry.npmjs.org/v2-postinstall-dep/-/v2-postinstall-dep-2.0.0.tgz"
),
"integrity": "sha512-fake",
},
@@ -187,8 +188,7 @@ def test_v2_v3_lockfile_format_support(tmp_path: Path):
head = _write(tmp_path / "head.json", _v2_lockfile(head_pkgs, head_deps))
result = _run(base, head)
assert result.returncode == 1, (
- f"expected exit 1 for v2 lockfile, got {result.returncode}; "
- f"stderr:\n{result.stderr}"
+ f"expected exit 1 for v2 lockfile, got {result.returncode}; " f"stderr:\n{result.stderr}"
)
assert "v2-postinstall-dep" in result.stderr
diff --git a/tests/security/test_scan_npm_packages.py b/tests/security/test_scan_npm_packages.py
index c632c618b0..fb575b730d 100644
--- a/tests/security/test_scan_npm_packages.py
+++ b/tests/security/test_scan_npm_packages.py
@@ -106,16 +106,10 @@ def test_blocked_npm_versions_complete():
table = snp.BLOCKED_NPM_VERSIONS
tanstack_keys = [k for k in table if k.startswith("@tanstack/")]
assert len(tanstack_keys) == 42, (
- f"expected 42 @tanstack/* entries, got {len(tanstack_keys)}: "
- f"{sorted(tanstack_keys)}"
+ f"expected 42 @tanstack/* entries, got {len(tanstack_keys)}: " f"{sorted(tanstack_keys)}"
)
assert "@opensearch-project/opensearch" in table
- assert table["@opensearch-project/opensearch"] == {
- "3.5.3",
- "3.6.2",
- "3.7.0",
- "3.8.0",
- }
+ assert table["@opensearch-project/opensearch"] == {"3.5.3", "3.6.2", "3.7.0", "3.8.0"}
squawk = [k for k in table if k.startswith("@squawk/")]
assert len(squawk) >= 22, (
f"expected at least 22 @squawk/* entries (full safedep.io enumeration), "
diff --git a/tests/security/test_scan_packages.py b/tests/security/test_scan_packages.py
index 6ef10f12eb..b35d89ce48 100644
--- a/tests/security/test_scan_packages.py
+++ b/tests/security/test_scan_packages.py
@@ -118,9 +118,7 @@ def test_clean_wheel_no_findings():
str(FIXTURES / "clean_wheel.whl"),
"clean_fixture",
)
- assert (
- findings == []
- ), f"unexpected findings on clean wheel: {[str(f) for f in findings]}"
+ assert findings == [], f"unexpected findings on clean wheel: {[str(f) for f in findings]}"
# ---------------------------------------------------------------------------
@@ -245,8 +243,7 @@ def test_archive_corruption_produces_critical_finding(tmp_path):
assert findings, "scan_archive returned 0 findings on corrupt wheel"
corrupted = [f for f in findings if f.check == "archive_corrupted"]
assert corrupted, (
- "no archive_corrupted finding; got "
- f"{[(f.severity, f.check) for f in findings]}"
+ "no archive_corrupted finding; got " f"{[(f.severity, f.check) for f in findings]}"
)
assert all(f.severity == sp.CRITICAL for f in corrupted)
diff --git a/tests/studio/_playwright_robust.py b/tests/studio/_playwright_robust.py
index b190b2b3e1..831153bf96 100644
--- a/tests/studio/_playwright_robust.py
+++ b/tests/studio/_playwright_robust.py
@@ -182,9 +182,7 @@ def wait_for_health(
# but accept any 200 -- different Studio builds report differently.
if status == 200:
if info is not None:
- info(
- f"health pre-flight OK: status=200, body keys={list((body or {}).keys())}"
- )
+ info(f"health pre-flight OK: status=200, body keys={list((body or {}).keys())}")
return True
time.sleep(0.5)
if info is not None:
@@ -230,9 +228,7 @@ def recover_or_replace_page(
info(f"recovery: page.is_closed() check failed: {exc!r}")
if goto_url is not None:
try:
- page.goto(
- goto_url, wait_until = "domcontentloaded", timeout = default_timeout_ms
- )
+ page.goto(goto_url, wait_until = "domcontentloaded", timeout = default_timeout_ms)
if settle_networkidle:
try:
page.wait_for_load_state("networkidle", timeout = 30_000)
diff --git a/tests/studio/install/smoke_test_llama_prebuilt.py b/tests/studio/install/smoke_test_llama_prebuilt.py
index d87537dc94..f7fd58aaa4 100644
--- a/tests/studio/install/smoke_test_llama_prebuilt.py
+++ b/tests/studio/install/smoke_test_llama_prebuilt.py
@@ -15,9 +15,7 @@ INSTALLER_PATH = PACKAGE_ROOT / "studio" / "install_llama_prebuilt.py"
def load_installer_module():
- spec = importlib.util.spec_from_file_location(
- "studio_install_llama_prebuilt", INSTALLER_PATH
- )
+ spec = importlib.util.spec_from_file_location("studio_install_llama_prebuilt", INSTALLER_PATH)
if spec is None or spec.loader is None:
raise RuntimeError(f"unable to load installer module from {INSTALLER_PATH}")
module = importlib.util.module_from_spec(spec)
@@ -112,17 +110,13 @@ def main() -> int:
published_release_tag = args.published_release_tag,
)
print(f"[smoke] PASS install_dir={install_dir}")
- print(
- "[smoke] note=This was a real prebuilt install into an isolated temp directory."
- )
+ print("[smoke] note=This was a real prebuilt install into an isolated temp directory.")
return installer.EXIT_SUCCESS
except SystemExit as exc:
code = int(exc.code) if isinstance(exc.code, int) else installer.EXIT_ERROR
if code == installer.EXIT_FALLBACK:
print(f"[smoke] FALLBACK install_dir={install_dir}")
- print(
- "[smoke] note=Prebuilt path failed and would fall back to source build in setup."
- )
+ print("[smoke] note=Prebuilt path failed and would fall back to source build in setup.")
print(installer.collect_system_report(host, choice, install_dir))
else:
print(f"[smoke] ERROR exit_code={code} install_dir={install_dir}")
diff --git a/tests/studio/install/smoke_test_parallel_studio_home.py b/tests/studio/install/smoke_test_parallel_studio_home.py
index 133591fb33..c4fc250655 100644
--- a/tests/studio/install/smoke_test_parallel_studio_home.py
+++ b/tests/studio/install/smoke_test_parallel_studio_home.py
@@ -86,12 +86,7 @@ def _free_port() -> int:
def _run_one_install(
- label: str,
- repo: Path,
- studio_home: Path,
- fake_home: Path,
- uv_cache: Path,
- log_path: Path,
+ label: str, repo: Path, studio_home: Path, fake_home: Path, uv_cache: Path, log_path: Path
) -> tuple[str, int]:
studio_home.mkdir(parents = True, exist_ok = True)
fake_home.mkdir(parents = True, exist_ok = True)
@@ -159,12 +154,14 @@ def _wait_for_health(port: int, timeout: float) -> dict:
except (urllib.error.URLError, ConnectionError, OSError) as e:
last_err = e
time.sleep(HEALTH_POLL_INTERVAL_S)
- raise TestFailure(
- f"port {port}: /api/health never returned 200 (last_err={last_err})"
- )
+ raise TestFailure(f"port {port}: /api/health never returned 200 (last_err={last_err})")
-def _http_status(port: int, path: str, timeout: float = 5.0) -> int:
+def _http_status(
+ port: int,
+ path: str,
+ timeout: float = 5.0,
+) -> int:
url = f"http://127.0.0.1:{port}{path}"
try:
with urllib.request.urlopen(url, timeout = timeout) as r:
@@ -211,9 +208,7 @@ def _check_install_layout(label: str, studio_home: Path) -> dict:
raise TestFailure(f"[{label}] launch-studio.sh kept @@DATA_DIR@@ placeholder")
expected_data_dir_line = f"DATA_DIR='{studio_home}/share'"
if expected_data_dir_line not in launcher:
- raise TestFailure(
- f"[{label}] launch-studio.sh missing {expected_data_dir_line!r}"
- )
+ raise TestFailure(f"[{label}] launch-studio.sh missing {expected_data_dir_line!r}")
return {"label": label, "studio_home": str(studio_home), "install_id": install_id}
@@ -230,9 +225,7 @@ def _check_fake_home_clean(fake_home: Path) -> None:
]
leaked = [str(p) for p in forbidden if (fake_home / p).exists()]
if leaked:
- raise TestFailure(
- f"redirected HOME picked up persistent install pollution: {leaked}"
- )
+ raise TestFailure(f"redirected HOME picked up persistent install pollution: {leaked}")
def _backend_pid_python(pid: int) -> Path | None:
@@ -256,9 +249,7 @@ def run(n_installs: int, keep: bool) -> int:
repo = PACKAGE_ROOT
if not (repo / "install.sh").is_file():
- raise TestFailure(
- f"install.sh not found at {repo}; " "run from a clone of unslothai/unsloth"
- )
+ raise TestFailure(f"install.sh not found at {repo}; run from a clone of unslothai/unsloth")
test_root = Path(tempfile.mkdtemp(prefix = "unsloth_studio_clash_"))
_log(f"test root: {test_root}")
@@ -346,8 +337,7 @@ def run(n_installs: int, keep: bool) -> int:
raise TestFailure(f"[{label}] chat_only is not true under --no-torch")
if health["studio_root_id"] in seen_root_ids:
raise TestFailure(
- f"[{label}] studio_root_id collision at runtime: "
- f"{health['studio_root_id']}"
+ f"[{label}] studio_root_id collision at runtime: " f"{health['studio_root_id']}"
)
seen_root_ids.add(health["studio_root_id"])
@@ -358,9 +348,7 @@ def run(n_installs: int, keep: bool) -> int:
exe = _backend_pid_python(proc.pid)
if exe is not None:
- expected_python = (
- studio_home / "unsloth_studio" / "bin" / "python"
- ).resolve()
+ expected_python = (studio_home / "unsloth_studio" / "bin" / "python").resolve()
if exe != expected_python:
raise TestFailure(
f"[{label}] PID {proc.pid} exe={exe}, expected {expected_python}"
@@ -370,10 +358,7 @@ def run(n_installs: int, keep: bool) -> int:
if len(versions) != 1:
raise TestFailure(f"version mismatch across installs: {versions}")
- _log(
- f"PASS: all install + runtime invariants hold "
- f"(version={next(iter(versions))})"
- )
+ _log(f"PASS: all install + runtime invariants hold " f"(version={next(iter(versions))})")
return 0
except TestFailure as e:
diff --git a/tests/studio/install/test_install_llama_prebuilt_logic.py b/tests/studio/install/test_install_llama_prebuilt_logic.py
index 4a427d6f54..de3808469c 100644
--- a/tests/studio/install/test_install_llama_prebuilt_logic.py
+++ b/tests/studio/install/test_install_llama_prebuilt_logic.py
@@ -12,9 +12,7 @@ import pytest
PACKAGE_ROOT = Path(__file__).resolve().parents[3]
MODULE_PATH = PACKAGE_ROOT / "studio" / "install_llama_prebuilt.py"
-SPEC = importlib.util.spec_from_file_location(
- "studio_install_llama_prebuilt", MODULE_PATH
-)
+SPEC = importlib.util.spec_from_file_location("studio_install_llama_prebuilt", MODULE_PATH)
assert SPEC is not None and SPEC.loader is not None
INSTALL_LLAMA_PREBUILT = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = INSTALL_LLAMA_PREBUILT
@@ -274,12 +272,8 @@ def test_validate_prebuilt_choice_creates_repo_shaped_linux_install(
"preflight_linux_installed_binaries",
lambda *args, **kwargs: None,
)
- monkeypatch.setattr(
- INSTALL_LLAMA_PREBUILT, "validate_quantize", lambda *args, **kwargs: None
- )
- monkeypatch.setattr(
- INSTALL_LLAMA_PREBUILT, "validate_server", lambda *args, **kwargs: None
- )
+ monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "validate_quantize", lambda *args, **kwargs: None)
+ monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "validate_server", lambda *args, **kwargs: None)
host = HostInfo(
system = "Linux",
@@ -381,9 +375,7 @@ def test_simple_linux_direct_release_uses_published_source_checksums_for_branch(
INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name(
source_commit
): ApprovedArtifactHash(
- asset_name = INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name(
- source_commit
- ),
+ asset_name = INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name(source_commit),
sha256 = "b" * 64,
repo = "ggml-org/llama.cpp",
kind = "exact-source",
@@ -429,9 +421,7 @@ def test_simple_linux_direct_release_uses_published_source_checksums_for_branch(
assert plan.approved_checksums.source_commit == source_commit
assert plan.attempts[0].expected_sha256 == "a" * 64
source_repo, source_ref, _source_archive, exact_source = (
- INSTALL_LLAMA_PREBUILT.preferred_source_archive(
- plan.approved_checksums, plan.llama_tag
- )
+ INSTALL_LLAMA_PREBUILT.preferred_source_archive(plan.approved_checksums, plan.llama_tag)
)
assert source_repo == "ggml-org/llama.cpp"
assert source_ref == source_commit
@@ -468,9 +458,7 @@ def test_simple_linux_direct_release_honors_torch_cudart_preference(
["cuda13", "cuda12"],
{
"cuda13": ["/usr/local/lib/python3.13/site-packages/nvidia/cu13/lib"],
- "cuda12": [
- "/venv/lib/python3.13/site-packages/nvidia/cuda_runtime/lib"
- ],
+ "cuda12": ["/venv/lib/python3.13/site-packages/nvidia/cuda_runtime/lib"],
},
),
)
@@ -517,24 +505,20 @@ def test_simple_linux_direct_release_honors_torch_cudart_preference(
[
# Missing source_commit.
(
- lambda c: setattr(c, "source_commit", None)
- or setattr(c, "source_commit_short", None),
+ lambda c: setattr(c, "source_commit", None) or setattr(c, "source_commit_short", None),
"exact source provenance",
),
# source_commit present, but no exact-source archive hash.
(
lambda c: c.artifacts.pop(
- INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name(
- c.source_commit
- ),
+ INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name(c.source_commit),
None,
),
"exact source provenance",
),
# source_commit + exact-source archive present, but no source_repo.
(
- lambda c: setattr(c, "source_repo", None)
- or setattr(c, "source_repo_url", None),
+ lambda c: setattr(c, "source_repo", None) or setattr(c, "source_repo_url", None),
"exact source provenance",
),
],
@@ -545,9 +529,7 @@ def test_simple_linux_direct_release_honors_torch_cudart_preference(
],
)
def test_simple_linux_direct_release_rejects_branch_without_exact_source_metadata(
- monkeypatch: pytest.MonkeyPatch,
- mutate,
- expected_match,
+ monkeypatch: pytest.MonkeyPatch, mutate, expected_match
):
source_commit = "25b1bc9c2f9aa0a390b968ee1ffd9ff01340a3fe"
release = {
@@ -583,9 +565,7 @@ def test_simple_linux_direct_release_rejects_branch_without_exact_source_metadat
INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name(
source_commit
): ApprovedArtifactHash(
- asset_name = INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name(
- source_commit
- ),
+ asset_name = INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name(source_commit),
sha256 = "b" * 64,
repo = "ggml-org/llama.cpp",
kind = "exact-source",
@@ -646,9 +626,7 @@ def test_simple_linux_direct_release_keeps_legacy_b_tag_path_without_checksums(
}
def unexpected_checksum_load(repo: str, release_tag: str):
- raise AssertionError(
- "legacy b-tag direct releases should not require checksum metadata"
- )
+ raise AssertionError("legacy b-tag direct releases should not require checksum metadata")
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
@@ -741,12 +719,8 @@ def test_validate_prebuilt_choice_creates_repo_shaped_windows_install(
"preflight_linux_installed_binaries",
lambda *args, **kwargs: None,
)
- monkeypatch.setattr(
- INSTALL_LLAMA_PREBUILT, "validate_quantize", lambda *args, **kwargs: None
- )
- monkeypatch.setattr(
- INSTALL_LLAMA_PREBUILT, "validate_server", lambda *args, **kwargs: None
- )
+ monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "validate_quantize", lambda *args, **kwargs: None)
+ monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "validate_server", lambda *args, **kwargs: None)
host = HostInfo(
system = "Windows",
@@ -809,9 +783,7 @@ def test_validate_prebuilt_choice_creates_repo_shaped_windows_install(
def test_activate_install_tree_restores_existing_install_after_activation_failure(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
- capsys: pytest.CaptureFixture[str],
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
):
install_dir = tmp_path / "llama.cpp"
install_dir.mkdir()
@@ -839,9 +811,7 @@ def test_activate_install_tree_restores_existing_install_after_activation_failur
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
"confirm_install_tree",
- lambda *_args, **_kwargs: (_ for _ in ()).throw(
- RuntimeError("activation confirm failed")
- ),
+ lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("activation confirm failed")),
)
with pytest.raises(
@@ -862,9 +832,7 @@ def test_activate_install_tree_restores_existing_install_after_activation_failur
def test_activate_install_tree_cleans_all_paths_when_rollback_restore_fails(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
- capsys: pytest.CaptureFixture[str],
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
):
install_dir = tmp_path / "llama.cpp"
install_dir.mkdir()
@@ -892,9 +860,7 @@ def test_activate_install_tree_cleans_all_paths_when_rollback_restore_fails(
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
"confirm_install_tree",
- lambda *_args, **_kwargs: (_ for _ in ()).throw(
- RuntimeError("activation confirm failed")
- ),
+ lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("activation confirm failed")),
)
original_replace = INSTALL_LLAMA_PREBUILT.os.replace
@@ -921,10 +887,7 @@ def test_activate_install_tree_cleans_all_paths_when_rollback_restore_fails(
captured = capsys.readouterr()
output = captured.out + captured.err
assert "rollback after failed activation also failed: restore failed" in output
- assert (
- "cleaning staging, install, and rollback paths before source build fallback"
- in output
- )
+ assert "cleaning staging, install, and rollback paths before source build fallback" in output
assert "removing failed install path" in output
assert "removing rollback path" in output
@@ -1108,9 +1071,7 @@ def write_linux_install_shape(install_dir: Path) -> None:
(runtime_dir / "libggml-base.so.0").write_bytes(b"DLL")
(runtime_dir / "libggml-cpu-x64.so.0").write_bytes(b"DLL")
(runtime_dir / "libmtmd.so.0").write_bytes(b"DLL")
- (install_dir / "convert_hf_to_gguf.py").write_text(
- "#!/usr/bin/env python3\n", encoding = "utf-8"
- )
+ (install_dir / "convert_hf_to_gguf.py").write_text("#!/usr/bin/env python3\n", encoding = "utf-8")
(install_dir / "gguf-py" / "gguf").mkdir(parents = True, exist_ok = True)
@@ -1134,9 +1095,7 @@ def write_windows_install_shape(
(runtime_dir / "cudart64_12.dll").write_bytes(b"DLL")
(runtime_dir / "cublas64_12.dll").write_bytes(b"DLL")
(runtime_dir / "cublasLt64_12.dll").write_bytes(b"DLL")
- (install_dir / "convert_hf_to_gguf.py").write_text(
- "#!/usr/bin/env python3\n", encoding = "utf-8"
- )
+ (install_dir / "convert_hf_to_gguf.py").write_text("#!/usr/bin/env python3\n", encoding = "utf-8")
(install_dir / "gguf-py" / "gguf").mkdir(parents = True, exist_ok = True)
@@ -1159,9 +1118,7 @@ def write_macos_install_shape(
(runtime_dir / "libggml.0.dylib").write_bytes(b"DLL")
if include_libmtmd:
(runtime_dir / "libmtmd.0.dylib").write_bytes(b"DLL")
- (install_dir / "convert_hf_to_gguf.py").write_text(
- "#!/usr/bin/env python3\n", encoding = "utf-8"
- )
+ (install_dir / "convert_hf_to_gguf.py").write_text("#!/usr/bin/env python3\n", encoding = "utf-8")
(install_dir / "gguf-py" / "gguf").mkdir(parents = True, exist_ok = True)
@@ -1240,8 +1197,7 @@ def test_existing_install_matches_plan_false_without_fingerprint(tmp_path: Path)
install_dir.mkdir()
write_linux_install_shape(install_dir)
(install_dir / "UNSLOTH_PREBUILT_INFO.json").write_text(
- json.dumps({"tag": "b9001", "asset": "llama-b9001-bin-ubuntu-x64.tar.gz"})
- + "\n",
+ json.dumps({"tag": "b9001", "asset": "llama-b9001-bin-ubuntu-x64.tar.gz"}) + "\n",
encoding = "utf-8",
)
@@ -1304,9 +1260,7 @@ def test_existing_install_matches_plan_false_with_malformed_metadata(tmp_path: P
install_dir = tmp_path / "llama.cpp"
install_dir.mkdir()
write_linux_install_shape(install_dir)
- (install_dir / "UNSLOTH_PREBUILT_INFO.json").write_text(
- "{not-json\n", encoding = "utf-8"
- )
+ (install_dir / "UNSLOTH_PREBUILT_INFO.json").write_text("{not-json\n", encoding = "utf-8")
host = HostInfo(
system = "Linux",
@@ -1437,9 +1391,7 @@ def test_existing_install_matches_plan_windows_cpu_requires_llama_dll(tmp_path:
def test_existing_install_matches_plan_windows_cuda_requires_cuda_dll(tmp_path: Path):
install_dir = tmp_path / "llama.cpp"
install_dir.mkdir()
- write_windows_install_shape(
- install_dir, include_llama_dll = True, include_cuda_dll = True
- )
+ write_windows_install_shape(install_dir, include_llama_dll = True, include_cuda_dll = True)
host = HostInfo(
system = "Windows",
@@ -1508,9 +1460,7 @@ def test_existing_install_matches_plan_windows_cuda_requires_cuda_dll(tmp_path:
assert existing_install_matches_plan(install_dir, host, plan) is False
-def test_existing_install_matches_plan_windows_cuda_paired_requires_cudart(
- tmp_path: Path,
-):
+def test_existing_install_matches_plan_windows_cuda_paired_requires_cudart(tmp_path: Path):
"""When the choice ships a paired cudart bundle (#5106), the install
is considered stale unless cudart64_*.dll and cublas64_*.dll are
actually on disk. Otherwise existing broken installs would keep
@@ -1626,9 +1576,7 @@ def test_existing_install_matches_plan_windows_cuda_paired_requires_cudart(
assert existing_install_matches_plan(install_dir, host, plan) is False
-def test_existing_install_matches_plan_windows_cuda_unpaired_skips_cudart_check(
- tmp_path: Path,
-):
+def test_existing_install_matches_plan_windows_cuda_unpaired_skips_cudart_check(tmp_path: Path):
"""If the choice has no paired runtime archive (manifest dropped it,
or upstream did not ship cudart), legacy installs without cudart on
disk must still pass the health check -- otherwise the installer
@@ -1708,9 +1656,7 @@ def test_existing_install_matches_plan_windows_cuda_unpaired_skips_cudart_check(
assert existing_install_matches_plan(install_dir, host, plan) is True
-def test_existing_install_fingerprint_changes_when_cudart_pair_added(
- tmp_path: Path,
-):
+def test_existing_install_fingerprint_changes_when_cudart_pair_added(tmp_path: Path):
"""Existing pre-#5322 Windows CUDA installs (no paired cudart) must
be treated as stale once the choice gains a runtime archive,
otherwise the fingerprint match would keep skipping the reinstall
@@ -1985,9 +1931,7 @@ def test_install_prebuilt_skips_download_when_existing_install_matches(
INSTALL_LLAMA_PREBUILT,
"download_validation_model",
lambda *args, **kwargs: (_ for _ in ()).throw(
- AssertionError(
- "matching install should skip before validation model download"
- )
+ AssertionError("matching install should skip before validation model download")
),
)
@@ -2503,9 +2447,7 @@ def test_install_prebuilt_same_tag_upstream_failure_uses_older_unsloth_release_p
(staging_dir / "marker.txt").write_text("ready\n")
return attempts[0], staging_dir, initial_fallback_used
- monkeypatch.setattr(
- INSTALL_LLAMA_PREBUILT, "validate_prebuilt_attempts", fake_validate
- )
+ monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "validate_prebuilt_attempts", fake_validate)
activated = {}
monkeypatch.setattr(
@@ -2523,10 +2465,7 @@ def test_install_prebuilt_same_tag_upstream_failure_uses_older_unsloth_release_p
install_prebuilt(install_dir, "latest", "unslothai/llama.cpp", "")
- assert attempted == [
- ("b9002", "release-2", "upstream"),
- ("b9001", "release-1", "upstream"),
- ]
+ assert attempted == [("b9002", "release-2", "upstream"), ("b9001", "release-1", "upstream")]
assert activated["install_dir"] == install_dir
@@ -2535,7 +2474,11 @@ def io_bytes(data: bytes):
def add_bytes_to_tar(
- archive: tarfile.TarFile, name: str, data: bytes, *, mode: int = 0o644
+ archive: tarfile.TarFile,
+ name: str,
+ data: bytes,
+ *,
+ mode: int = 0o644,
) -> None:
info = tarfile.TarInfo(name)
info.size = len(data)
@@ -2550,9 +2493,7 @@ def add_symlink_to_tar(archive: tarfile.TarFile, name: str, target: str) -> None
archive.addfile(info)
-def test_existing_install_matches_choice_fails_when_install_tree_incomplete(
- tmp_path: Path,
-):
+def test_existing_install_matches_choice_fails_when_install_tree_incomplete(tmp_path: Path):
"""confirm_install_tree guard rejects installs missing critical files."""
install_dir = tmp_path / "llama.cpp"
install_dir.mkdir()
@@ -2641,9 +2582,7 @@ def test_existing_install_matches_choice_fails_when_install_tree_incomplete(
)
-def test_existing_install_matches_choice_fails_when_install_tree_incomplete_macos(
- tmp_path: Path,
-):
+def test_existing_install_matches_choice_fails_when_install_tree_incomplete_macos(tmp_path: Path):
"""confirm_install_tree guard rejects macOS arm64 installs missing critical files."""
install_dir = tmp_path / "llama.cpp"
install_dir.mkdir()
@@ -2780,9 +2719,7 @@ def test_paired_runtime_dll_patterns_excludes_executables() -> None:
assert paired_runtime_dll_patterns(non_windows) == []
-def test_runtime_overlay_cannot_overwrite_main_archive_payload(
- tmp_path: Path,
-) -> None:
+def test_runtime_overlay_cannot_overwrite_main_archive_payload(tmp_path: Path) -> None:
"""End-to-end: a malformed runtime archive containing
``llama-server.exe`` alongside the real cudart DLLs must NOT
replace the main archive's ``llama-server.exe``.
@@ -2846,15 +2783,20 @@ def test_runtime_overlay_cannot_overwrite_main_archive_payload(
orig_download = INSTALL_LLAMA_PREBUILT.download_file_verified
- def fake_download(url, target_path, *, expected_sha256 = None, label = None, **kw):
+ def fake_download(
+ url,
+ target_path,
+ *,
+ expected_sha256 = None,
+ label = None,
+ **kw,
+ ):
src = main_zip if "cudart" not in url else runtime_zip
_shutil.copy2(src, target_path)
if expected_sha256:
actual = hashlib.sha256(Path(target_path).read_bytes()).hexdigest()
if actual != expected_sha256:
- raise INSTALL_LLAMA_PREBUILT.PrebuiltFallback(
- f"sha256 mismatch on {label}"
- )
+ raise INSTALL_LLAMA_PREBUILT.PrebuiltFallback(f"sha256 mismatch on {label}")
INSTALL_LLAMA_PREBUILT.download_file_verified = fake_download
try:
@@ -2866,16 +2808,13 @@ def test_runtime_overlay_cannot_overwrite_main_archive_payload(
server = release_dir / "llama-server.exe"
assert server.exists()
assert server.read_bytes() == b"MAIN-SERVER", (
- "runtime archive overwrote main llama-server.exe; "
- f"got {server.read_bytes()!r}"
+ "runtime archive overwrote main llama-server.exe; " f"got {server.read_bytes()!r}"
)
for name in ("cudart64_12.dll", "cublas64_12.dll", "cublasLt64_12.dll"):
assert (release_dir / name).exists(), f"missing {name}"
-def test_linux_runtime_overlay_copies_llama_tool_impl_libraries(
- tmp_path: Path,
-) -> None:
+def test_linux_runtime_overlay_copies_llama_tool_impl_libraries(tmp_path: Path) -> None:
install_from_archives = INSTALL_LLAMA_PREBUILT.install_from_archives
work = tmp_path / "work"
@@ -2939,14 +2878,19 @@ def test_linux_runtime_overlay_copies_llama_tool_impl_libraries(
orig_download = INSTALL_LLAMA_PREBUILT.download_file_verified
- def fake_download(url, target_path, *, expected_sha256 = None, label = None, **kw):
+ def fake_download(
+ url,
+ target_path,
+ *,
+ expected_sha256 = None,
+ label = None,
+ **kw,
+ ):
_shutil.copy2(bundle, target_path)
if expected_sha256:
actual = hashlib.sha256(Path(target_path).read_bytes()).hexdigest()
if actual != expected_sha256:
- raise INSTALL_LLAMA_PREBUILT.PrebuiltFallback(
- f"sha256 mismatch on {label}"
- )
+ raise INSTALL_LLAMA_PREBUILT.PrebuiltFallback(f"sha256 mismatch on {label}")
INSTALL_LLAMA_PREBUILT.download_file_verified = fake_download
try:
@@ -2964,9 +2908,7 @@ def test_linux_runtime_overlay_copies_llama_tool_impl_libraries(
assert not (runtime_dir / "llama-cli").exists()
-def test_python_runtime_dirs_covers_cu13_and_library_bin(
- monkeypatch, tmp_path: Path
-) -> None:
+def test_python_runtime_dirs_covers_cu13_and_library_bin(monkeypatch, tmp_path: Path) -> None:
"""Installer-side runtime DLL discovery must scan the same path
set as the backend ``_windows_pip_nvidia_dll_dirs``: legacy
``nvidia//bin``, current ``nvidia//bin/x86_64``
diff --git a/tests/studio/install/test_llama_pr_force_and_source.py b/tests/studio/install/test_llama_pr_force_and_source.py
index 2d7c038861..17c58cba51 100644
--- a/tests/studio/install/test_llama_pr_force_and_source.py
+++ b/tests/studio/install/test_llama_pr_force_and_source.py
@@ -35,7 +35,10 @@ requires_pwsh = pytest.mark.skipif(not PWSH_AVAILABLE, reason = "pwsh not availa
# Helpers
# ---------------------------------------------------------------------------
def run_bash(
- script: str, *, timeout: int = 60, env: dict | None = None
+ script: str,
+ *,
+ timeout: int = 60,
+ env: dict | None = None,
) -> subprocess.CompletedProcess:
"""Run a bash script fragment and return the CompletedProcess.
60s default tolerates slow shell startup on heavily-loaded CI
@@ -53,7 +56,10 @@ def run_bash(
def run_pwsh(
- script: str, *, timeout: int = 60, env: dict | None = None
+ script: str,
+ *,
+ timeout: int = 60,
+ env: dict | None = None,
) -> subprocess.CompletedProcess:
"""Run a PowerShell script fragment and return the CompletedProcess.
60s default tolerates slow pwsh startup on heavily-loaded CI
@@ -383,10 +389,7 @@ class TestSourcePatternsSh:
assert '_DEFAULT_LLAMA_PR_FORCE=""' in self.content
def test_has_default_source(self):
- assert (
- '_DEFAULT_LLAMA_SOURCE="https://github.com/ggml-org/llama.cpp"'
- in self.content
- )
+ assert '_DEFAULT_LLAMA_SOURCE="https://github.com/ggml-org/llama.cpp"' in self.content
def test_has_pr_force_env_read(self):
assert "UNSLOTH_LLAMA_PR_FORCE" in self.content
@@ -416,8 +419,7 @@ class TestSourcePatternsSh:
def test_clone_urls_parameterized_pr_path(self):
"""PR clone path uses ${_LLAMA_SOURCE}.git, not hardcoded URL."""
pr_clone_idx = self.content.index(
- 'if [ -n "$_LLAMA_PR" ]; then\n'
- ' run_quiet_no_exit "clone llama.cpp"'
+ 'if [ -n "$_LLAMA_PR" ]; then\n run_quiet_no_exit "clone llama.cpp"'
)
else_idx = self.content.index("else\n", pr_clone_idx)
pr_block = self.content[pr_clone_idx:else_idx]
@@ -437,9 +439,7 @@ class TestSourcePatternsSh:
lines = self.content.splitlines()
for i, line in enumerate(lines, 1):
if "git clone" in line and "ggml-org/llama.cpp.git" in line:
- pytest.fail(
- f"Line {i} has hardcoded ggml-org clone URL: {line.strip()}"
- )
+ pytest.fail(f"Line {i} has hardcoded ggml-org clone URL: {line.strip()}")
# =========================================================================
@@ -456,10 +456,7 @@ class TestSourcePatternsPs1:
assert '$DefaultLlamaPrForce = ""' in self.content
def test_has_default_source(self):
- assert (
- '$DefaultLlamaSource = "https://github.com/ggml-org/llama.cpp"'
- in self.content
- )
+ assert '$DefaultLlamaSource = "https://github.com/ggml-org/llama.cpp"' in self.content
def test_has_pr_force_env_read(self):
assert "$env:UNSLOTH_LLAMA_PR_FORCE" in self.content
@@ -469,10 +466,7 @@ class TestSourcePatternsPs1:
assert "$LlamaSource = $DefaultLlamaSource" in self.content
def test_release_repo_override_removed(self):
- assert (
- "$HelperReleaseRepo = if ($env:UNSLOTH_LLAMA_RELEASE_REPO)"
- not in self.content
- )
+ assert "$HelperReleaseRepo = if ($env:UNSLOTH_LLAMA_RELEASE_REPO)" not in self.content
assert '$HelperReleaseRepo = "ggml-org/llama.cpp"' in self.content
def test_force_compile_skips_prebuilt_resolution_early(self):
@@ -491,9 +485,7 @@ class TestSourcePatternsPs1:
def test_clone_urls_parameterized_pr_path(self):
"""PR clone path uses $LlamaSource.git, not hardcoded URL."""
- pr_idx = self.content.index(
- "if ($LlamaPr) {\n", self.content.index("Cloning llama.cpp")
- )
+ pr_idx = self.content.index("if ($LlamaPr) {\n", self.content.index("Cloning llama.cpp"))
else_idx = self.content.index("} else {", pr_idx)
pr_block = self.content[pr_idx:else_idx]
assert '"$LlamaSource.git"' in pr_block
@@ -511,9 +503,7 @@ class TestSourcePatternsPs1:
lines = self.content.splitlines()
for i, line in enumerate(lines, 1):
if "git clone" in line and "ggml-org/llama.cpp.git" in line:
- pytest.fail(
- f"Line {i} has hardcoded ggml-org clone URL: {line.strip()}"
- )
+ pytest.fail(f"Line {i} has hardcoded ggml-org clone URL: {line.strip()}")
# =========================================================================
diff --git a/tests/studio/install/test_macos_version_compat.py b/tests/studio/install/test_macos_version_compat.py
index 1b87e5af65..7f93b295eb 100644
--- a/tests/studio/install/test_macos_version_compat.py
+++ b/tests/studio/install/test_macos_version_compat.py
@@ -20,9 +20,7 @@ import pytest
PACKAGE_ROOT = Path(__file__).resolve().parents[3]
MODULE_PATH = PACKAGE_ROOT / "studio" / "install_llama_prebuilt.py"
-SPEC = importlib.util.spec_from_file_location(
- "studio_install_llama_prebuilt_macos", MODULE_PATH
-)
+SPEC = importlib.util.spec_from_file_location("studio_install_llama_prebuilt_macos", MODULE_PATH)
assert SPEC is not None and SPEC.loader is not None
ILP = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = ILP
@@ -54,7 +52,12 @@ def make_macos_host(macos_version, *, arm64 = True):
)
-def thin_macho(minos = (14, 0), *, cputype = _CPU_TYPE_ARM64, build_version = True):
+def thin_macho(
+ minos = (14, 0),
+ *,
+ cputype = _CPU_TYPE_ARM64,
+ build_version = True,
+):
"""Synthesize a minimal little-endian 64-bit Mach-O carrying a macOS
minimum-version load command."""
encoded = (minos[0] << 16) | (minos[1] << 8)
@@ -139,10 +142,7 @@ class TestMachoMinimumMacos:
)
)
assert ILP.macho_minimum_macos(path, make_macos_host((14, 0))) == (14, 0)
- assert ILP.macho_minimum_macos(path, make_macos_host((26, 0), arm64 = False)) == (
- 26,
- 0,
- )
+ assert ILP.macho_minimum_macos(path, make_macos_host((26, 0), arm64 = False)) == (26, 0)
def test_non_macho_returns_none(self, tmp_path):
path = tmp_path / "script.sh"
@@ -185,23 +185,17 @@ class TestPreflightMacosInstalledBinaries:
def test_rejects_too_new_dylib(self, tmp_path):
install_dir, binaries = self._install_dir(tmp_path, (26, 0))
with pytest.raises(PrebuiltFallback, match = "newer macOS"):
- ILP.preflight_macos_installed_binaries(
- binaries, install_dir, make_macos_host((14, 0))
- )
+ ILP.preflight_macos_installed_binaries(binaries, install_dir, make_macos_host((14, 0)))
def test_accepts_compatible_prebuilt(self, tmp_path):
install_dir, binaries = self._install_dir(tmp_path, (14, 0))
# Must not raise on a macOS 15 host.
- ILP.preflight_macos_installed_binaries(
- binaries, install_dir, make_macos_host((15, 5))
- )
+ ILP.preflight_macos_installed_binaries(binaries, install_dir, make_macos_host((15, 5)))
def test_skips_when_host_version_unknown(self, tmp_path):
install_dir, binaries = self._install_dir(tmp_path, (26, 0))
# Unknown host version -> defer to runtime validation, do not raise.
- ILP.preflight_macos_installed_binaries(
- binaries, install_dir, make_macos_host(None)
- )
+ ILP.preflight_macos_installed_binaries(binaries, install_dir, make_macos_host(None))
def test_noop_on_non_macos_host(self, tmp_path):
install_dir, binaries = self._install_dir(tmp_path, (26, 0))
diff --git a/tests/studio/install/test_pr4562_bugfixes.py b/tests/studio/install/test_pr4562_bugfixes.py
index 34b144a905..5b1c4a44e5 100644
--- a/tests/studio/install/test_pr4562_bugfixes.py
+++ b/tests/studio/install/test_pr4562_bugfixes.py
@@ -29,9 +29,7 @@ import pytest
# ---------------------------------------------------------------------------
PACKAGE_ROOT = Path(__file__).resolve().parents[3]
MODULE_PATH = PACKAGE_ROOT / "studio" / "install_llama_prebuilt.py"
-SPEC = importlib.util.spec_from_file_location(
- "studio_install_llama_prebuilt", MODULE_PATH
-)
+SPEC = importlib.util.spec_from_file_location("studio_install_llama_prebuilt", MODULE_PATH)
assert SPEC is not None and SPEC.loader is not None
MOD = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = MOD
@@ -74,7 +72,12 @@ def make_host(*, system: str) -> HostInfo:
BASH = "/bin/bash"
-def run_bash(script: str, *, timeout: int = 10, env: dict | None = None) -> str:
+def run_bash(
+ script: str,
+ *,
+ timeout: int = 10,
+ env: dict | None = None,
+) -> str:
"""Run a bash script fragment and return its stdout."""
run_env = os.environ.copy()
if env:
@@ -113,9 +116,7 @@ class TestBinaryEnvCrossPlatform:
env = binary_env(binary_path, install_dir, host)
ld_dirs = env["LD_LIBRARY_PATH"].split(os.pathsep)
assert str(bin_dir) in ld_dirs, f"build/bin not in LD_LIBRARY_PATH: {ld_dirs}"
- assert (
- str(install_dir) in ld_dirs
- ), f"install_dir not in LD_LIBRARY_PATH: {ld_dirs}"
+ assert str(install_dir) in ld_dirs, f"install_dir not in LD_LIBRARY_PATH: {ld_dirs}"
def test_linux_binary_parent_comes_before_install_dir(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
@@ -134,9 +135,7 @@ class TestBinaryEnvCrossPlatform:
ld_dirs = env["LD_LIBRARY_PATH"].split(os.pathsep)
bin_idx = ld_dirs.index(str(bin_dir))
install_idx = ld_dirs.index(str(install_dir))
- assert (
- bin_idx < install_idx
- ), "binary_path.parent should come before install_dir"
+ assert bin_idx < install_idx, "binary_path.parent should come before install_dir"
def test_linux_deduplicates_when_binary_parent_equals_install_dir(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
@@ -195,17 +194,13 @@ class TestBinaryEnvCrossPlatform:
binary_path.write_bytes(b"MZ")
host = make_host(system = "Windows")
- monkeypatch.setattr(
- MOD, "windows_runtime_dirs_for_runtime_line", lambda _rt: []
- )
+ monkeypatch.setattr(MOD, "windows_runtime_dirs_for_runtime_line", lambda _rt: [])
env = binary_env(binary_path, install_dir, host)
path_dirs = env["PATH"].split(os.pathsep)
assert str(bin_dir) in path_dirs, f"build/bin/Release not in PATH: {path_dirs}"
- def test_macos_sets_dyld_library_path(
- self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
- ):
+ def test_macos_sets_dyld_library_path(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
install_dir = tmp_path / "llama.cpp"
install_dir.mkdir(parents = True)
bin_dir = install_dir / "build" / "bin"
@@ -218,12 +213,8 @@ class TestBinaryEnvCrossPlatform:
env = binary_env(binary_path, install_dir, host)
dyld_parts = [p for p in env["DYLD_LIBRARY_PATH"].split(os.pathsep) if p]
- assert (
- str(bin_dir) in dyld_parts
- ), f"build/bin not in DYLD_LIBRARY_PATH: {dyld_parts}"
- assert (
- str(install_dir) in dyld_parts
- ), f"install_dir not in DYLD_LIBRARY_PATH: {dyld_parts}"
+ assert str(bin_dir) in dyld_parts, f"build/bin not in DYLD_LIBRARY_PATH: {dyld_parts}"
+ assert str(install_dir) in dyld_parts, f"install_dir not in DYLD_LIBRARY_PATH: {dyld_parts}"
# binary_path.parent (build/bin) should come before install_dir
assert dyld_parts.index(str(bin_dir)) < dyld_parts.index(str(install_dir))
@@ -303,7 +294,11 @@ class TestResolveRequestedLlamaTag:
):
captured = {}
- def fake_resolve(requested_tag, published_repo, published_release_tag = ""):
+ def fake_resolve(
+ requested_tag,
+ published_repo,
+ published_release_tag = "",
+ ):
captured["requested_tag"] = requested_tag
captured["published_repo"] = published_repo
captured["published_release_tag"] = published_release_tag
@@ -350,9 +345,7 @@ class TestResolveRequestedLlamaTag:
class TestFetchJsonRetries:
- def test_fetch_json_retries_invalid_github_api_json(
- self, monkeypatch: pytest.MonkeyPatch
- ):
+ def test_fetch_json_retries_invalid_github_api_json(self, monkeypatch: pytest.MonkeyPatch):
calls = {"count": 0}
def fake_download_bytes(url, **kwargs):
@@ -593,11 +586,7 @@ class TestLatestTagResolution:
""")
def _run_resolve(
- self,
- tmp_path: Path,
- requested_tag: str,
- resolved_tag: str,
- resolve_status: int,
+ self, tmp_path: Path, requested_tag: str, resolved_tag: str, resolve_status: int
) -> str:
script = self.RESOLVE_TEMPLATE.format(
requested_tag = requested_tag,
@@ -691,10 +680,7 @@ class TestSourceCodePatterns:
content = SETUP_SH.read_text()
assert "--resolve-source-build" not in content
assert "--resolve-install-tag" not in content
- assert (
- '--resolve-llama-tag latest --published-repo "ggml-org/llama.cpp"'
- in content
- )
+ assert '--resolve-llama-tag latest --published-repo "ggml-org/llama.cpp"' in content
assert "--output-format json" in content
assert "_RESOLVED_SOURCE_URL" in content
assert "_RESOLVED_SOURCE_REF_KIND" in content
@@ -758,9 +744,7 @@ class TestSourceCodePatterns:
# Delivered via NVCC_PREPEND_FLAGS (covers the configure-time compiler
# probe too), not embedded in the word-split CMAKE_ARGS string.
assert "export NVCC_PREPEND_FLAGS=" in content
- cmake_args_lines = [
- line for line in content.splitlines() if "CMAKE_ARGS=" in line
- ]
+ cmake_args_lines = [line for line in content.splitlines() if "CMAKE_ARGS=" in line]
assert all(
"-allow-unsupported-compiler" not in line for line in cmake_args_lines
), "flag must stay out of CMAKE_ARGS (bash word-splitting safety)"
@@ -776,9 +760,7 @@ class TestSourceCodePatterns:
# Delivered via the process environment, not the $CmakeArgs array, so it
# reaches both the configure-time compiler probe and `cmake --build`.
assert "$env:NVCC_PREPEND_FLAGS" in content
- cmake_args_lines = [
- line for line in content.splitlines() if "$CmakeArgs +=" in line
- ]
+ cmake_args_lines = [line for line in content.splitlines() if "$CmakeArgs +=" in line]
assert all(
"-allow-unsupported-compiler" not in line for line in cmake_args_lines
), "flag must not be pushed into the $CmakeArgs array"
@@ -794,15 +776,10 @@ class TestSourceCodePatterns:
def test_macos_arm64_cpu_fallback_args_exclude_rpath(self):
"""CPU fallback args must NOT contain Metal-only RPATH flags at runtime."""
- script = (
- '_IS_MACOS_ARM64=true\nNVCC_PATH=""\nGPU_BACKEND=""\n'
- + _GPU_BACKEND_FRAGMENT
- )
+ script = '_IS_MACOS_ARM64=true\nNVCC_PATH=""\nGPU_BACKEND=""\n' + _GPU_BACKEND_FRAGMENT
output = run_bash(script)
fallback_line = next(
- line
- for line in output.splitlines()
- if line.startswith("CPU_FALLBACK_CMAKE_ARGS=")
+ line for line in output.splitlines() if line.startswith("CPU_FALLBACK_CMAKE_ARGS=")
)
assert "-DGGML_METAL=OFF" in fallback_line
assert (
@@ -823,8 +800,7 @@ class TestSourceCodePatterns:
assert (
"x86_64"
not in content[
- content.find("-DGGML_METAL=ON") - 200 : content.find("-DGGML_METAL=ON")
- + 200
+ content.find("-DGGML_METAL=ON") - 200 : content.find("-DGGML_METAL=ON") + 200
]
)
@@ -854,9 +830,7 @@ class TestSourceCodePatterns:
# Allow git pull in other contexts
context = "\n".join(lines[max(0, i - 5) : i + 5])
if "LlamaCppDir" in context:
- pytest.fail(
- f"Found 'git pull' in llama.cpp build section at line {i+1}"
- )
+ pytest.fail(f"Found 'git pull' in llama.cpp build section at line {i+1}")
def test_setup_ps1_prebuilt_install_uses_simple_policy_only(self):
"""PS1 prebuilt path should use the simplified helper install entrypoint."""
@@ -883,8 +857,7 @@ class TestSourceCodePatterns:
assert "--resolve-source-build" not in content
assert "--resolve-install-tag" not in content
assert (
- '"--resolve-llama-tag", "latest", "--published-repo", "ggml-org/llama.cpp"'
- in content
+ '"--resolve-llama-tag", "latest", "--published-repo", "ggml-org/llama.cpp"' in content
)
assert '--output-format", "json"' in content
assert "$ResolvedSourceUrl" in content
@@ -898,10 +871,7 @@ class TestSourceCodePatterns:
block = content[max(0, install_idx - 800) : install_idx + 800]
assert "$PSNativeCommandUseErrorActionPreference = $false" in block
assert "$restoreNativeErrorPreference = $true" in block
- assert (
- "$PSNativeCommandUseErrorActionPreference = $previousNativeErrorPreference"
- in block
- )
+ assert "$PSNativeCommandUseErrorActionPreference = $previousNativeErrorPreference" in block
def test_setup_ps1_helper_disables_error_action_abort(self):
"""Helper resolution should suppress terminating NativeCommandError on PS 5.1."""
@@ -922,9 +892,7 @@ class TestSourceCodePatterns:
"""The unconstrained nvcc fallback should not sort toolkit dirs lexicographically."""
content = SETUP_PS1.read_text()
assert "Sort-Object Name | Select-Object -Last 1" not in content
- assert (
- "Sort-Object { [version]($_.Name -replace '^v','') } -Descending" in content
- )
+ assert "Sort-Object { [version]($_.Name -replace '^v','') } -Descending" in content
def test_binary_env_linux_has_binary_parent(self):
"""The Linux branch of binary_env should include binary_path.parent."""
@@ -985,10 +953,7 @@ class TestMacOSMetalBuildLogic:
def test_macos_arm64_cmake_args_contain_metal_flags(self):
"""macOS arm64 should enable Metal, not CUDA."""
- script = (
- '_IS_MACOS_ARM64=true\nNVCC_PATH=""\nGPU_BACKEND=""\n'
- + _GPU_BACKEND_FRAGMENT
- )
+ script = '_IS_MACOS_ARM64=true\nNVCC_PATH=""\nGPU_BACKEND=""\n' + _GPU_BACKEND_FRAGMENT
output = run_bash(script)
assert "-DGGML_METAL=ON" in output
assert "-DGGML_CUDA=ON" not in output
@@ -996,10 +961,7 @@ class TestMacOSMetalBuildLogic:
def test_intel_macos_no_metal_flags(self):
"""Intel macOS (not arm64) should not get Metal flags."""
- script = (
- '_IS_MACOS_ARM64=false\nNVCC_PATH=""\nGPU_BACKEND=""\n'
- + _GPU_BACKEND_FRAGMENT
- )
+ script = '_IS_MACOS_ARM64=false\nNVCC_PATH=""\nGPU_BACKEND=""\n' + _GPU_BACKEND_FRAGMENT
output = run_bash(script)
assert "-DGGML_METAL=ON" not in output
assert "BUILD_DESC=building (CPU)" in output
@@ -1085,18 +1047,14 @@ class TestMacOSMetalBuildLogic:
# Verify cmake args: first call has Metal ON, second has Metal OFF
calls = calls_file.read_text().splitlines()
assert len(calls) >= 2, f"Expected >= 2 cmake calls, got {len(calls)}"
- assert (
- "-DGGML_METAL=ON" in calls[0]
- ), f"First cmake call should have Metal ON: {calls[0]}"
+ assert "-DGGML_METAL=ON" in calls[0], f"First cmake call should have Metal ON: {calls[0]}"
assert (
"-DGGML_METAL=OFF" in calls[1]
), f"Second cmake call should have Metal OFF: {calls[1]}"
assert (
"-DGGML_METAL=ON" not in calls[1]
), f"Second cmake call should NOT have Metal ON: {calls[1]}"
- assert (
- "@loader_path" not in calls[1]
- ), f"CPU fallback should not have RPATH: {calls[1]}"
+ assert "@loader_path" not in calls[1], f"CPU fallback should not have RPATH: {calls[1]}"
assert (
"-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON" not in calls[1]
), f"CPU fallback should not have RPATH build flag: {calls[1]}"
@@ -1204,9 +1162,7 @@ class TestMacOSMetalBuildLogic:
# Third call: re-configure with Metal OFF and no RPATH flags
assert "-DGGML_METAL=OFF" in calls[2]
assert "-DGGML_METAL=ON" not in calls[2]
- assert (
- "@loader_path" not in calls[2]
- ), f"CPU fallback should not have RPATH: {calls[2]}"
+ assert "@loader_path" not in calls[2], f"CPU fallback should not have RPATH: {calls[2]}"
assert (
"-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON" not in calls[2]
), f"CPU fallback should not have RPATH build flag: {calls[2]}"
diff --git a/tests/studio/install/test_rocm_support.py b/tests/studio/install/test_rocm_support.py
index 1d6e4f4c51..bcfc42c69f 100644
--- a/tests/studio/install/test_rocm_support.py
+++ b/tests/studio/install/test_rocm_support.py
@@ -40,9 +40,7 @@ _normalize_forwarded_gfx = prebuilt_mod._normalize_forwarded_gfx
# install_python_stack.py
_STACK_PATH = PACKAGE_ROOT / "studio" / "install_python_stack.py"
-_STACK_SPEC = importlib.util.spec_from_file_location(
- "studio_install_python_stack", _STACK_PATH
-)
+_STACK_SPEC = importlib.util.spec_from_file_location("studio_install_python_stack", _STACK_PATH)
assert _STACK_SPEC is not None and _STACK_SPEC.loader is not None
stack_mod = importlib.util.module_from_spec(_STACK_SPEC)
sys.modules[_STACK_SPEC.name] = stack_mod
@@ -304,9 +302,7 @@ class TestResolveUpstreamAssetChoice:
def test_rocm_linux_no_prebuilt_falls_back(self, mock_assets):
"""AMD ROCm host should fall back to source build when no ROCm prebuilt exists."""
# Remove the ROCm asset from available assets
- assets_without_rocm = {
- k: v for k, v in UPSTREAM_ASSETS.items() if "rocm" not in k
- }
+ assets_without_rocm = {k: v for k, v in UPSTREAM_ASSETS.items() if "rocm" not in k}
mock_assets.return_value = assets_without_rocm
host = rocm_host()
with pytest.raises(PrebuiltFallback, match = "ROCm detected"):
@@ -573,9 +569,7 @@ class TestEnsureRocmTorch:
@patch.object(stack_mod, "_has_usable_nvidia_gpu", return_value = False)
@patch.object(stack_mod, "_has_rocm_gpu", return_value = True)
@patch.object(stack_mod, "_detect_rocm_version", return_value = (7, 1))
- def test_torch_already_has_cuda_skips(
- self, mock_ver, mock_gpu, mock_nvidia, mock_pip
- ):
+ def test_torch_already_has_cuda_skips(self, mock_ver, mock_gpu, mock_nvidia, mock_pip):
"""If torch already has CUDA, should skip ROCm reinstall."""
mock_probe = MagicMock()
mock_probe.returncode = 0
@@ -589,9 +583,7 @@ class TestEnsureRocmTorch:
@patch.object(stack_mod, "_has_usable_nvidia_gpu", return_value = False)
@patch.object(stack_mod, "_has_rocm_gpu", return_value = True)
@patch.object(stack_mod, "_detect_rocm_version", return_value = (7, 1))
- def test_torch_already_has_hip_skips(
- self, mock_ver, mock_gpu, mock_nvidia, mock_pip
- ):
+ def test_torch_already_has_hip_skips(self, mock_ver, mock_gpu, mock_nvidia, mock_pip):
"""If torch already has HIP, should skip ROCm reinstall."""
mock_probe = MagicMock()
mock_probe.returncode = 0
@@ -629,9 +621,7 @@ class TestEnsureRocmTorch:
@patch.object(stack_mod, "_has_usable_nvidia_gpu", return_value = False)
@patch.object(stack_mod, "_has_rocm_gpu", return_value = True)
@patch.object(stack_mod, "_detect_rocm_version", return_value = (6, 3))
- def test_rocm_63_selects_correct_tag(
- self, mock_ver, mock_gpu, mock_nvidia, mock_pip
- ):
+ def test_rocm_63_selects_correct_tag(self, mock_ver, mock_gpu, mock_nvidia, mock_pip):
"""ROCm 6.3 should select rocm6.3 tag."""
mock_probe = MagicMock()
mock_probe.returncode = 0
@@ -698,9 +688,7 @@ class TestEnsureRocmTorch:
):
"""Probe subprocess timeout should not crash; should proceed to reinstall."""
with patch("os.path.isdir", return_value = True):
- with patch(
- "subprocess.run", side_effect = subprocess.TimeoutExpired("python", 30)
- ):
+ with patch("subprocess.run", side_effect = subprocess.TimeoutExpired("python", 30)):
_ensure_rocm_torch()
# If probe times out, the function should treat torch as unusable and reinstall
# both torch (via pip_install) and bitsandbytes (via pip_install_try).
@@ -788,25 +776,19 @@ class TestHardwareRocmFlag:
def test_hardware_py_has_is_rocm(self):
"""hardware.py should define IS_ROCM."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
assert "IS_ROCM: bool" in source and "False" in source
def test_hardware_py_sets_is_rocm_on_hip(self):
"""detect_hardware() should set IS_ROCM when torch.version.hip is set."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
assert 'torch.version, "hip"' in source or "torch.version.hip" in source
def test_hardware_py_still_returns_cuda_for_rocm(self):
"""DeviceType should remain CUDA even on ROCm -- no DeviceType.ROCM."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
# Ensure ROCM is NOT a DeviceType member
enum_section = source.split("class DeviceType")[1].split("\n\n")[0]
@@ -814,17 +796,13 @@ class TestHardwareRocmFlag:
def test_hardware_py_has_rocm_in_package_versions(self):
"""get_package_versions() should include 'rocm' key."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
assert '"rocm"' in source
def test_hardware_py_device_type_cuda_references_intact(self):
"""All existing DeviceType.CUDA references should still be present."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
# Key functions that must still reference DeviceType.CUDA
assert "DeviceType.CUDA" in source
@@ -832,26 +810,20 @@ class TestHardwareRocmFlag:
def test_is_rocm_exported_from_init(self):
"""IS_ROCM should be exported from hardware __init__.py."""
- init_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "__init__.py"
- )
+ init_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "__init__.py"
source = init_path.read_text(encoding = "utf-8")
assert "IS_ROCM" in source
def test_is_rocm_in_all_list(self):
"""IS_ROCM should be in __all__ list in __init__.py."""
- init_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "__init__.py"
- )
+ init_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "__init__.py"
source = init_path.read_text(encoding = "utf-8")
# Extract __all__ section
assert '"IS_ROCM"' in source
def test_get_package_versions_returns_rocm_key(self):
"""get_package_versions() source should return both 'cuda' and 'rocm' keys."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
# Find the get_package_versions function body
func_start = source.find("def get_package_versions")
@@ -866,22 +838,16 @@ class TestHardwareRocmFlag:
Windows ROCm where torch.distributed ships without that helper, causing
a warning: 'module torch.distributed has no attribute is_torchelastic_launched'.
"""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
assert "is_torchelastic_launched" in source
def test_distributed_stubs_cover_core_helpers(self):
"""_determine_attention_impl_for_gpu_estimate must stub the four core distributed helpers."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
for attr in ("is_initialized", "is_available", "get_rank", "get_world_size"):
- assert (
- attr in source
- ), f"distributed stub for '{attr}' missing from hardware.py"
+ assert attr in source, f"distributed stub for '{attr}' missing from hardware.py"
# =============================================================================
@@ -947,12 +913,8 @@ class TestInstallShStructure:
nvidia_call = body.find("_has_usable_nvidia_gpu")
no_nvidia_branch = body.find('if [ -z "$_smi" ]')
rocm_call = body.find("_has_amd_rocm_gpu")
- assert (
- nvidia_call >= 0
- ), "get_torch_index_url should call _has_usable_nvidia_gpu"
- assert (
- no_nvidia_branch >= 0
- ), "get_torch_index_url should gate ROCm on no-nvidia-smi"
+ assert nvidia_call >= 0, "get_torch_index_url should call _has_usable_nvidia_gpu"
+ assert no_nvidia_branch >= 0, "get_torch_index_url should gate ROCm on no-nvidia-smi"
assert (
rocm_call > no_nvidia_branch
), "ROCm detection should sit inside the 'no nvidia-smi' branch"
@@ -1013,9 +975,7 @@ class TestInstallShStructure:
continue
# Remove POSIX character classes [[:foo:]] before checking for [[ ]]
cleaned = re.sub(r"\[\[:[a-z]+:\]\]", "", line)
- assert (
- "[[" not in cleaned
- ), f"get_torch_index_url line {i} uses non-POSIX [["
+ assert "[[" not in cleaned, f"get_torch_index_url line {i} uses non-POSIX [["
def test_no_arithmetic_expansion_in_rocm_block(self):
"""ROCm detection block should not use (( )) (bash-only)."""
@@ -1063,8 +1023,7 @@ class TestLiveRegression:
[
"bash",
"-c",
- "nvidia-smi -L 2>/dev/null | "
- "awk '/^GPU[[:space:]]+[0-9]+:/{f=1} END{exit !f}'",
+ "nvidia-smi -L 2>/dev/null | awk '/^GPU[[:space:]]+[0-9]+:/{f=1} END{exit !f}'",
],
capture_output = True,
)
@@ -1098,9 +1057,7 @@ class TestLiveRegression:
# Load worker.py module
_WORKER_PATH = PACKAGE_ROOT / "studio" / "backend" / "core" / "training" / "worker.py"
-_EXPORT_WORKER_PATH = (
- PACKAGE_ROOT / "studio" / "backend" / "core" / "export" / "worker.py"
-)
+_EXPORT_WORKER_PATH = PACKAGE_ROOT / "studio" / "backend" / "core" / "export" / "worker.py"
# The torchao Windows-ROCm stub was de-duplicated out of the export/training
# workers into a shared module; both workers now call into it.
_TORCHAO_STUB_PATH = PACKAGE_ROOT / "studio" / "backend" / "core" / "_torchao_stub.py"
@@ -1123,21 +1080,22 @@ class TestWorkerRocmMambaSsm:
source = _WHEEL_UTILS_PATH.read_text(encoding = "utf-8")
assert "getattr(torch.version, 'hip', None)" in source
- def test_direct_wheel_url_returns_none_without_cuda_major(self):
+ def test_direct_wheel_url_returns_none_without_cuda_major(self, monkeypatch):
"""_direct_wheel_url should return None when cuda_major is empty (ROCm)."""
# Load module for function access
- _worker_spec = importlib.util.spec_from_file_location(
- "test_worker", _WORKER_PATH
- )
+ _worker_spec = importlib.util.spec_from_file_location("test_worker", _WORKER_PATH)
assert _worker_spec is not None and _worker_spec.loader is not None
worker_mod = importlib.util.module_from_spec(_worker_spec)
- # Mock all the imports worker.py needs
- sys.modules["structlog"] = MagicMock()
- sys.modules["loggers"] = MagicMock()
- sys.modules["loggers"].get_logger = MagicMock(return_value = MagicMock())
- sys.modules["utils"] = MagicMock()
- sys.modules["utils.hardware"] = MagicMock()
+ # Stub worker.py's imports via monkeypatch so the fakes (notably a
+ # non-package "utils") are undone and don't break later tests that
+ # import the real utils.* package.
+ loggers_mock = MagicMock()
+ loggers_mock.get_logger = MagicMock(return_value = MagicMock())
+ monkeypatch.setitem(sys.modules, "structlog", MagicMock())
+ monkeypatch.setitem(sys.modules, "loggers", loggers_mock)
+ monkeypatch.setitem(sys.modules, "utils", MagicMock())
+ monkeypatch.setitem(sys.modules, "utils.hardware", MagicMock())
try:
_worker_spec.loader.exec_module(worker_mod)
@@ -1203,15 +1161,16 @@ class TestAmdGpuMonitoring:
assert "def get_primary_gpu_utilization" in source
assert "def get_visible_gpu_utilization" in source
- def test_amd_smi_json_parsing(self):
+ def test_amd_smi_json_parsing(self, monkeypatch):
"""Verify _extract_gpu_metrics parses amd-smi JSON correctly."""
amd_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "amd.py"
_amd_spec = importlib.util.spec_from_file_location("test_amd", amd_path)
assert _amd_spec is not None and _amd_spec.loader is not None
amd_mod = importlib.util.module_from_spec(_amd_spec)
- sys.modules["loggers"] = MagicMock()
- sys.modules["loggers"].get_logger = MagicMock(return_value = MagicMock())
+ loggers_mock = MagicMock()
+ loggers_mock.get_logger = MagicMock(return_value = MagicMock())
+ monkeypatch.setitem(sys.modules, "loggers", loggers_mock)
try:
_amd_spec.loader.exec_module(amd_mod)
@@ -1248,8 +1207,9 @@ class TestAmdGpuMonitoring:
assert _amd_spec is not None and _amd_spec.loader is not None
amd_mod = importlib.util.module_from_spec(_amd_spec)
- sys.modules["loggers"] = MagicMock()
- sys.modules["loggers"].get_logger = MagicMock(return_value = MagicMock())
+ loggers_mock = MagicMock()
+ loggers_mock.get_logger = MagicMock(return_value = MagicMock())
+ monkeypatch.setitem(sys.modules, "loggers", loggers_mock)
try:
_amd_spec.loader.exec_module(amd_mod)
@@ -1287,15 +1247,16 @@ class TestAmdGpuMonitoring:
assert result["gpu_utilization_pct"] == 50.0
assert result["temperature_c"] == 65.0
- def test_amd_smi_not_found_returns_unavailable(self):
+ def test_amd_smi_not_found_returns_unavailable(self, monkeypatch):
"""get_primary_gpu_utilization returns available=False when amd-smi is missing."""
amd_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "amd.py"
_amd_spec = importlib.util.spec_from_file_location("test_amd3", amd_path)
assert _amd_spec is not None and _amd_spec.loader is not None
amd_mod = importlib.util.module_from_spec(_amd_spec)
- sys.modules["loggers"] = MagicMock()
- sys.modules["loggers"].get_logger = MagicMock(return_value = MagicMock())
+ loggers_mock = MagicMock()
+ loggers_mock.get_logger = MagicMock(return_value = MagicMock())
+ monkeypatch.setitem(sys.modules, "loggers", loggers_mock)
try:
_amd_spec.loader.exec_module(amd_mod)
@@ -1306,15 +1267,16 @@ class TestAmdGpuMonitoring:
result = amd_mod.get_primary_gpu_utilization()
assert result["available"] is False
- def test_amd_timeout_returns_unavailable(self):
+ def test_amd_timeout_returns_unavailable(self, monkeypatch):
"""get_primary_gpu_utilization handles timeout gracefully."""
amd_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "amd.py"
_amd_spec = importlib.util.spec_from_file_location("test_amd4", amd_path)
assert _amd_spec is not None and _amd_spec.loader is not None
amd_mod = importlib.util.module_from_spec(_amd_spec)
- sys.modules["loggers"] = MagicMock()
- sys.modules["loggers"].get_logger = MagicMock(return_value = MagicMock())
+ loggers_mock = MagicMock()
+ loggers_mock.get_logger = MagicMock(return_value = MagicMock())
+ monkeypatch.setitem(sys.modules, "loggers", loggers_mock)
try:
_amd_spec.loader.exec_module(amd_mod)
@@ -1340,9 +1302,7 @@ class TestHardwareAmdBranching:
def test_hardware_imports_amd_module(self):
"""hardware.py should import from amd module when IS_ROCM."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
assert "from . import amd" in source
@@ -1350,17 +1310,13 @@ class TestHardwareAmdBranching:
"""get_gpu_utilization should dispatch to amd.py via _smi_query
when IS_ROCM, and the dispatcher itself must check IS_ROCM and
import the amd backend."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
func_start = source.find("def get_gpu_utilization")
func_body = source[func_start : source.find("\ndef ", func_start + 1)]
assert '_smi_query("get_primary_gpu_utilization"' in func_body
smi = source[
- source.find("def _smi_query") : source.find(
- "\ndef ", source.find("def _smi_query") + 1
- )
+ source.find("def _smi_query") : source.find("\ndef ", source.find("def _smi_query") + 1)
]
assert "IS_ROCM" in smi
assert "from . import amd" in smi
@@ -1368,9 +1324,7 @@ class TestHardwareAmdBranching:
def test_hardware_branches_on_is_rocm_for_visible(self):
"""get_visible_gpu_utilization should dispatch to amd.py via
_smi_query when IS_ROCM."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
func_start = source.find("def get_visible_gpu_utilization")
func_body = source[func_start : source.find("\ndef ", func_start + 1)]
@@ -1380,18 +1334,14 @@ class TestHardwareAmdBranching:
assert _re.search(r'_smi_query\(\s*"get_visible_gpu_utilization"', func_body)
smi = source[
- source.find("def _smi_query") : source.find(
- "\ndef ", source.find("def _smi_query") + 1
- )
+ source.find("def _smi_query") : source.find("\ndef ", source.find("def _smi_query") + 1)
]
assert "IS_ROCM" in smi
assert "from . import amd" in smi
def test_hardware_branches_on_is_rocm_for_physical_count(self):
"""get_physical_gpu_count should try amd.py when IS_ROCM."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
func_start = source.find("def get_physical_gpu_count")
func_body = source[func_start : source.find("\ndef ", func_start + 1)]
@@ -1410,9 +1360,7 @@ class TestApplyGpuIdsRocmFallback:
def test_apply_gpu_ids_falls_back_to_torch_version_hip(self):
"""apply_gpu_ids should probe torch.version.hip when IS_ROCM is False and no ROCm env vars are set."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
func_start = source.find("def apply_gpu_ids")
func_body = source[func_start : source.find("\ndef ", func_start + 1)]
@@ -1420,9 +1368,7 @@ class TestApplyGpuIdsRocmFallback:
def test_apply_gpu_ids_sets_hip_and_rocr_visible_devices(self):
"""apply_gpu_ids should set both HIP_VISIBLE_DEVICES and ROCR_VISIBLE_DEVICES on ROCm."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
func_start = source.find("def apply_gpu_ids")
func_body = source[func_start : source.find("\ndef ", func_start + 1)]
@@ -1431,9 +1377,7 @@ class TestApplyGpuIdsRocmFallback:
def test_apply_gpu_ids_rocm_fallback_is_guarded_by_try_except(self):
"""torch import in apply_gpu_ids must be wrapped in try/except so a missing torch never crashes."""
- hw_path = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
- )
+ hw_path = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
source = hw_path.read_text(encoding = "utf-8")
func_start = source.find("def apply_gpu_ids")
func_body = source[func_start : source.find("\ndef ", func_start + 1)]
@@ -1584,9 +1528,7 @@ class TestWindowsRocmIndexUrl:
assert "repo.amd.com" in url
def test_mirror_env_var_overrides_base(self, monkeypatch):
- monkeypatch.setenv(
- "UNSLOTH_ROCM_WINDOWS_MIRROR", "https://my-mirror.example.com/rocm/whl"
- )
+ monkeypatch.setenv("UNSLOTH_ROCM_WINDOWS_MIRROR", "https://my-mirror.example.com/rocm/whl")
# Reload module-level constant by calling helper directly
url = stack_mod._windows_rocm_index_url("gfx1200")
# The env var is read at module load time for _ROCM_WINDOWS_INDEX_BASE,
@@ -1715,9 +1657,7 @@ class TestInstallBnbWindowsRocm:
with patch.dict(os.environ, {}, clear = False):
os.environ.pop("BNB_ROCM_VERSION", None)
with patch.object(stack_mod, "pip_install_try", return_value = True):
- with patch.object(
- stack_mod, "_detect_bnb_rocm_dll_ver", return_value = "72"
- ):
+ with patch.object(stack_mod, "_detect_bnb_rocm_dll_ver", return_value = "72"):
stack_mod._install_bnb_windows_rocm()
assert os.environ.get("BNB_ROCM_VERSION") == "72"
@@ -1726,9 +1666,7 @@ class TestInstallBnbWindowsRocm:
with patch.dict(os.environ, {}, clear = False):
os.environ.pop("BNB_ROCM_VERSION", None)
with patch.object(stack_mod, "pip_install_try", return_value = True):
- with patch.object(
- stack_mod, "_detect_bnb_rocm_dll_ver", return_value = "713"
- ):
+ with patch.object(stack_mod, "_detect_bnb_rocm_dll_ver", return_value = "713"):
stack_mod._install_bnb_windows_rocm()
assert os.environ.get("BNB_ROCM_VERSION") == "713"
@@ -1737,9 +1675,7 @@ class TestInstallBnbWindowsRocm:
with patch.dict(os.environ, {}, clear = False):
os.environ.pop("BNB_ROCM_VERSION", None)
with patch.object(stack_mod, "pip_install_try", return_value = True):
- with patch.object(
- stack_mod, "_detect_bnb_rocm_dll_ver", return_value = None
- ):
+ with patch.object(stack_mod, "_detect_bnb_rocm_dll_ver", return_value = None):
stack_mod._install_bnb_windows_rocm()
assert os.environ.get("BNB_ROCM_VERSION") == "72"
@@ -1757,7 +1693,6 @@ class TestDetectBnbRocmDllVer:
def test_returns_none_when_bnb_not_installed(self):
"""Returns None if bitsandbytes is not importable."""
import importlib.util
-
with patch.object(importlib.util, "find_spec", return_value = None):
assert stack_mod._detect_bnb_rocm_dll_ver() is None
@@ -1966,9 +1901,7 @@ class TestWorkerWindowsRocmPatches:
# entry-point function (not the trainer helper which has its own "# ── 2.").
idx_sec2 = source.find("# ── 2. Now import ML libraries")
assert idx_bnb != -1, "BNB_ROCM_VERSION not found in worker.py"
- assert (
- idx_sec2 != -1
- ), "'# ── 2. Now import ML libraries' marker not found in worker.py"
+ assert idx_sec2 != -1, "'# ── 2. Now import ML libraries' marker not found in worker.py"
assert idx_bnb < idx_sec2, (
"BNB_ROCM_VERSION must be set before section 2 ML imports "
f"(found at {idx_bnb}, section 2 at {idx_sec2})"
@@ -2262,16 +2195,12 @@ class TestHipSdkEnvPathResolution:
"""setup.ps1 must tell the user how to add the HIP bin dir to PATH."""
source = _SETUP_PS1_PATH.read_text(encoding = "utf-8")
# Should mention adding to PATH or SetEnvironmentVariable
- assert "PATH" in source and (
- "SetEnvironmentVariable" in source or "Add" in source
- )
+ assert "PATH" in source and ("SetEnvironmentVariable" in source or "Add" in source)
def test_install_provides_path_fix_hint(self):
"""install.ps1 must tell the user how to add the HIP bin dir to PATH."""
source = _INSTALL_PS1_PATH.read_text(encoding = "utf-8")
- assert "PATH" in source and (
- "SetEnvironmentVariable" in source or "Add" in source
- )
+ assert "PATH" in source and ("SetEnvironmentVariable" in source or "Add" in source)
# =============================================================================
@@ -2441,9 +2370,7 @@ class TestSetupShGccInstallDir:
# =============================================================================
_MAIN_PY_PATH = PACKAGE_ROOT / "studio" / "backend" / "main.py"
-_HARDWARE_PY_PATH = (
- PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
-)
+_HARDWARE_PY_PATH = PACKAGE_ROOT / "studio" / "backend" / "utils" / "hardware" / "hardware.py"
class TestServerStartupRocmFixes:
@@ -2657,9 +2584,7 @@ class TestApplyHostOverrides:
assert out.rocm_gfx_target is None
def test_malformed_forwarded_gfx_falls_back_to_has_rocm(self):
- out = _apply_host_overrides(
- cpu_host(), override_has_rocm = True, override_rocm_gfx = "junk"
- )
+ out = _apply_host_overrides(cpu_host(), override_has_rocm = True, override_rocm_gfx = "junk")
assert out.has_rocm is True
assert out.rocm_gfx_target is None
diff --git a/tests/studio/install/test_selection_logic.py b/tests/studio/install/test_selection_logic.py
index f1c3130a77..1ada35888c 100644
--- a/tests/studio/install/test_selection_logic.py
+++ b/tests/studio/install/test_selection_logic.py
@@ -22,9 +22,7 @@ import pytest
PACKAGE_ROOT = Path(__file__).resolve().parents[3]
MODULE_PATH = PACKAGE_ROOT / "studio" / "install_llama_prebuilt.py"
RUN_MODULE_PATH = PACKAGE_ROOT / "studio" / "backend" / "run.py"
-SPEC = importlib.util.spec_from_file_location(
- "studio_install_llama_prebuilt", MODULE_PATH
-)
+SPEC = importlib.util.spec_from_file_location("studio_install_llama_prebuilt", MODULE_PATH)
assert SPEC is not None and SPEC.loader is not None
INSTALL_LLAMA_PREBUILT = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = INSTALL_LLAMA_PREBUILT
@@ -49,15 +47,11 @@ supports_explicit_visible_device_matching = (
select_visible_gpu_rows = INSTALL_LLAMA_PREBUILT.select_visible_gpu_rows
compatible_linux_runtime_lines = INSTALL_LLAMA_PREBUILT.compatible_linux_runtime_lines
pick_windows_cuda_runtime = INSTALL_LLAMA_PREBUILT.pick_windows_cuda_runtime
-compatible_windows_runtime_lines = (
- INSTALL_LLAMA_PREBUILT.compatible_windows_runtime_lines
-)
+compatible_windows_runtime_lines = INSTALL_LLAMA_PREBUILT.compatible_windows_runtime_lines
runtime_line_from_cuda_version = INSTALL_LLAMA_PREBUILT.runtime_line_from_cuda_version
apply_approved_hashes = INSTALL_LLAMA_PREBUILT.apply_approved_hashes
linux_cuda_choice_from_release = INSTALL_LLAMA_PREBUILT.linux_cuda_choice_from_release
-parse_direct_linux_release_bundle = (
- INSTALL_LLAMA_PREBUILT.parse_direct_linux_release_bundle
-)
+parse_direct_linux_release_bundle = INSTALL_LLAMA_PREBUILT.parse_direct_linux_release_bundle
windows_cuda_attempts = INSTALL_LLAMA_PREBUILT.windows_cuda_attempts
resolve_upstream_asset_choice = INSTALL_LLAMA_PREBUILT.resolve_upstream_asset_choice
resolve_requested_install_tag = INSTALL_LLAMA_PREBUILT.resolve_requested_install_tag
@@ -66,19 +60,11 @@ resolve_install_release_plans = INSTALL_LLAMA_PREBUILT.resolve_install_release_p
resolve_published_release = INSTALL_LLAMA_PREBUILT.resolve_published_release
resolve_source_build_plan = INSTALL_LLAMA_PREBUILT.resolve_source_build_plan
validated_checksums_for_bundle = INSTALL_LLAMA_PREBUILT.validated_checksums_for_bundle
-parse_approved_release_checksums = (
- INSTALL_LLAMA_PREBUILT.parse_approved_release_checksums
-)
-published_release_matches_request = (
- INSTALL_LLAMA_PREBUILT.published_release_matches_request
-)
-exact_source_archive_logical_name = (
- INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name
-)
+parse_approved_release_checksums = INSTALL_LLAMA_PREBUILT.parse_approved_release_checksums
+published_release_matches_request = INSTALL_LLAMA_PREBUILT.published_release_matches_request
+exact_source_archive_logical_name = INSTALL_LLAMA_PREBUILT.exact_source_archive_logical_name
source_archive_logical_name = INSTALL_LLAMA_PREBUILT.source_archive_logical_name
-windows_cuda_upstream_asset_names = (
- INSTALL_LLAMA_PREBUILT.windows_cuda_upstream_asset_names
-)
+windows_cuda_upstream_asset_names = INSTALL_LLAMA_PREBUILT.windows_cuda_upstream_asset_names
env_int = INSTALL_LLAMA_PREBUILT.env_int
direct_upstream_release_plan = INSTALL_LLAMA_PREBUILT.direct_upstream_release_plan
_pinned_windows_cuda_fallback = INSTALL_LLAMA_PREBUILT._pinned_windows_cuda_fallback
@@ -89,9 +75,7 @@ _windows_cuda_attempt_covers_blackwell = (
)
resolve_release_asset_choice = INSTALL_LLAMA_PREBUILT.resolve_release_asset_choice
pinned_macos_release_tag = INSTALL_LLAMA_PREBUILT.pinned_macos_release_tag
-resolve_simple_install_release_plans = (
- INSTALL_LLAMA_PREBUILT.resolve_simple_install_release_plans
-)
+resolve_simple_install_release_plans = INSTALL_LLAMA_PREBUILT.resolve_simple_install_release_plans
def load_studio_run_module(monkeypatch):
@@ -244,9 +228,7 @@ def make_checksums_with_source(
kind = "upstream-source",
),
}
- normalized_source_commit = (
- source_commit.lower() if isinstance(source_commit, str) else None
- )
+ normalized_source_commit = source_commit.lower() if isinstance(source_commit, str) else None
if normalized_source_commit:
artifacts[exact_source_archive_logical_name(normalized_source_commit)] = (
ApprovedArtifactHash(
@@ -266,9 +248,7 @@ def make_checksums_with_source(
requested_source_ref = requested_source_ref,
resolved_source_ref = resolved_source_ref,
source_commit = normalized_source_commit,
- source_commit_short = normalized_source_commit[:7]
- if normalized_source_commit
- else None,
+ source_commit_short = normalized_source_commit[:7] if normalized_source_commit else None,
artifacts = artifacts,
)
@@ -363,10 +343,7 @@ class TestStudioLocalhostIpv6Warning:
lambda host, port, timeout = 1.0: True,
)
- assert (
- run_module._localhost_ipv6_mismatch_url("127.0.0.1", 8888)
- == "http://127.0.0.1:8888"
- )
+ assert run_module._localhost_ipv6_mismatch_url("127.0.0.1", 8888) == "http://127.0.0.1:8888"
@pytest.mark.parametrize("host", ["0.0.0.0", "::"])
def test_network_bind_suppresses_warning(self, monkeypatch, host):
@@ -431,9 +408,7 @@ class TestStudioLocalhostIpv6Warning:
monkeypatch.setattr(
run_module,
"_verify_global_reachability",
- lambda display_host, port: calls["reachability"].append(
- (display_host, port)
- ),
+ lambda display_host, port: calls["reachability"].append((display_host, port)),
)
return calls
@@ -456,9 +431,7 @@ class TestStudioLocalhostIpv6Warning:
def test_emit_startup_output_plain_localhost(self, monkeypatch):
run_module = load_studio_run_module(monkeypatch)
calls = self._wire_recorders(run_module, monkeypatch)
- monkeypatch.setattr(
- run_module, "_localhost_ipv6_mismatch_url", lambda host, port: None
- )
+ monkeypatch.setattr(run_module, "_localhost_ipv6_mismatch_url", lambda host, port: None)
run_module._emit_startup_output("127.0.0.1", 8888, "127.0.0.1")
@@ -471,9 +444,7 @@ class TestStudioLocalhostIpv6Warning:
def test_emit_startup_output_wildcard_runs_reachability(self, monkeypatch, host):
run_module = load_studio_run_module(monkeypatch)
calls = self._wire_recorders(run_module, monkeypatch)
- monkeypatch.setattr(
- run_module, "_localhost_ipv6_mismatch_url", lambda h, port: None
- )
+ monkeypatch.setattr(run_module, "_localhost_ipv6_mismatch_url", lambda h, port: None)
run_module._emit_startup_output(host, 8888, "203.0.113.5")
@@ -651,9 +622,7 @@ class TestParseDirectLinuxReleaseBundle:
names = [f"app-bTEST-linux-x64-{t}.tar.gz" for t in targets]
return {
"tag_name": "bTEST",
- "assets": [
- {"name": n, "browser_download_url": "https://x/" + n} for n in names
- ],
+ "assets": [{"name": n, "browser_download_url": "https://x/" + n} for n in names],
}
def _cuda_artifact(self, bundle):
@@ -885,9 +854,7 @@ class TestPublishedReleaseResolution:
def fake_load(repo, release_tag):
if release_tag == "v2.0":
raise PrebuiltFallback("checksum asset missing")
- return make_checksums_with_source(
- [], release_tag = "v1.0", upstream_tag = "b8999"
- )
+ return make_checksums_with_source([], release_tag = "v1.0", upstream_tag = "b8999")
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
@@ -917,9 +884,7 @@ class TestPublishedReleaseResolution:
),
)
- assert (
- resolve_requested_install_tag("b8508", "", "unslothai/llama.cpp") == "b8508"
- )
+ assert resolve_requested_install_tag("b8508", "", "unslothai/llama.cpp") == "b8508"
def test_concrete_tag_without_matching_release_raises(self, monkeypatch):
release = make_release([], release_tag = "release-b9000", upstream_tag = "b9000")
@@ -933,9 +898,7 @@ class TestPublishedReleaseResolution:
resolve_requested_install_tag("b8508", "", "unslothai/llama.cpp")
def test_pinned_release_must_match_requested_upstream_tag(self, monkeypatch):
- bundle = make_release(
- [], release_tag = "llama-prebuilt-latest", upstream_tag = "b9000"
- )
+ bundle = make_release([], release_tag = "llama-prebuilt-latest", upstream_tag = "b9000")
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
"pinned_published_release_bundle",
@@ -1110,15 +1073,13 @@ class TestSourceBuildPlanResolution:
assert plan.source_ref == "main"
assert plan.compatibility_upstream_tag == "b9000"
- def test_direct_main_request_without_published_release_uses_branch_kind(
- self, monkeypatch
- ):
+ def test_direct_main_request_without_published_release_uses_branch_kind(self, monkeypatch):
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
"resolve_published_release",
- lambda requested_tag, published_repo, published_release_tag = "": (
- _ for _ in ()
- ).throw(PrebuiltFallback("missing")),
+ lambda requested_tag, published_repo, published_release_tag = "": (_ for _ in ()).throw(
+ PrebuiltFallback("missing")
+ ),
)
plan = resolve_source_build_plan("main", "unslothai/llama.cpp")
@@ -1193,9 +1154,7 @@ class TestValidatedChecksumsForBundle:
def test_rejects_manifest_checksum_mismatch(self, monkeypatch):
bundle = make_release([], release_tag = "r1", upstream_tag = "b8508")
bundle.manifest_sha256 = "a" * 64
- checksums = make_checksums_with_source(
- [], release_tag = "r1", upstream_tag = "b8508"
- )
+ checksums = make_checksums_with_source([], release_tag = "r1", upstream_tag = "b8508")
checksums.artifacts[bundle.manifest_asset_name] = ApprovedArtifactHash(
asset_name = bundle.manifest_asset_name,
sha256 = "b" * 64,
@@ -1249,9 +1208,7 @@ class TestLinuxCudaChoiceFromRelease:
art12 = make_artifact("bundle-cuda12.tar.gz", runtime_line = "cuda12")
art13 = make_artifact("bundle-cuda13.tar.gz", runtime_line = "cuda13")
release = make_release([art12, art13])
- result = linux_cuda_choice_from_release(
- host, release, preferred_runtime_line = "cuda12"
- )
+ result = linux_cuda_choice_from_release(host, release, preferred_runtime_line = "cuda12")
assert result is not None
assert result.primary.runtime_line == "cuda12"
@@ -1260,9 +1217,7 @@ class TestLinuxCudaChoiceFromRelease:
host = make_host(driver_cuda_version = (12, 8))
art = make_artifact("bundle-cuda12.tar.gz", runtime_line = "cuda12")
release = make_release([art])
- result = linux_cuda_choice_from_release(
- host, release, preferred_runtime_line = "cuda13"
- )
+ result = linux_cuda_choice_from_release(host, release, preferred_runtime_line = "cuda13")
assert result is not None
assert result.primary.runtime_line == "cuda12"
log_entries = result.selection_log
@@ -1273,9 +1228,7 @@ class TestLinuxCudaChoiceFromRelease:
def test_exact_sm_match(self, monkeypatch):
mock_linux_runtime(monkeypatch, ["cuda12"])
host = make_host(compute_caps = ["86"])
- art = make_artifact(
- "bundle.tar.gz", supported_sms = ["75", "86", "89"], min_sm = 75, max_sm = 89
- )
+ art = make_artifact("bundle.tar.gz", supported_sms = ["75", "86", "89"], min_sm = 75, max_sm = 89)
release = make_release([art])
result = linux_cuda_choice_from_release(host, release)
assert result is not None
@@ -1284,9 +1237,7 @@ class TestLinuxCudaChoiceFromRelease:
def test_sm_not_in_supported_sms(self, monkeypatch):
mock_linux_runtime(monkeypatch, ["cuda12"])
host = make_host(compute_caps = ["86"])
- art = make_artifact(
- "bundle.tar.gz", supported_sms = ["75", "80", "89"], min_sm = 75, max_sm = 89
- )
+ art = make_artifact("bundle.tar.gz", supported_sms = ["75", "80", "89"], min_sm = 75, max_sm = 89)
release = make_release([art])
result = linux_cuda_choice_from_release(host, release)
assert result is None
@@ -1294,9 +1245,7 @@ class TestLinuxCudaChoiceFromRelease:
def test_sm_outside_min_range(self, monkeypatch):
mock_linux_runtime(monkeypatch, ["cuda12"])
host = make_host(compute_caps = ["50"])
- art = make_artifact(
- "bundle.tar.gz", supported_sms = ["50", "75", "86"], min_sm = 75, max_sm = 90
- )
+ art = make_artifact("bundle.tar.gz", supported_sms = ["50", "75", "86"], min_sm = 75, max_sm = 90)
release = make_release([art])
result = linux_cuda_choice_from_release(host, release)
assert result is None
@@ -1365,9 +1314,7 @@ class TestLinuxCudaChoiceFromRelease:
def test_multi_gpu_not_all_covered(self, monkeypatch):
mock_linux_runtime(monkeypatch, ["cuda12"])
host = make_host(compute_caps = ["50", "89"])
- art = make_artifact(
- "bundle.tar.gz", supported_sms = ["75", "89"], min_sm = 75, max_sm = 89
- )
+ art = make_artifact("bundle.tar.gz", supported_sms = ["75", "89"], min_sm = 75, max_sm = 89)
release = make_release([art])
result = linux_cuda_choice_from_release(host, release)
assert result is None
@@ -1559,9 +1506,7 @@ class TestBlackwellUltraSm103Coverage:
class TestResolveInstallAttempts:
- def test_windows_cuda_prefers_published_asset_from_selected_release(
- self, monkeypatch
- ):
+ def test_windows_cuda_prefers_published_asset_from_selected_release(self, monkeypatch):
host = make_host(system = "Windows", machine = "AMD64")
host.driver_cuda_version = (12, 4)
mock_windows_runtime(monkeypatch, ["cuda12"])
@@ -1605,9 +1550,7 @@ class TestResolveInstallAttempts:
INSTALL_LLAMA_PREBUILT,
"github_release_assets",
lambda repo, tag: (_ for _ in ()).throw(
- AssertionError(
- "published Windows CUDA choice should not query upstream"
- )
+ AssertionError("published Windows CUDA choice should not query upstream")
),
)
@@ -1629,9 +1572,7 @@ class TestResolveInstallAttempts:
host = make_host(system = "Windows", machine = "AMD64")
host.driver_cuda_version = (12, 4)
mock_windows_runtime(monkeypatch, ["cuda12"])
- release = make_release(
- [], release_tag = "llama-prebuilt-latest", upstream_tag = "b9000"
- )
+ release = make_release([], release_tag = "llama-prebuilt-latest", upstream_tag = "b9000")
checksums = make_checksums_with_source(
["llama-b9000-bin-win-cuda-12.4-x64.zip"],
release_tag = release.release_tag,
@@ -1692,9 +1633,7 @@ class TestResolveInstallAttempts:
has_physical_nvidia = False,
nvidia_smi = None,
)
- release = make_release(
- [], release_tag = "llama-prebuilt-latest", upstream_tag = "b9000"
- )
+ release = make_release([], release_tag = "llama-prebuilt-latest", upstream_tag = "b9000")
checksums = make_checksums_with_source(
["llama-b9000-bin-ubuntu-x64.tar.gz"],
release_tag = release.release_tag,
@@ -1735,9 +1674,7 @@ class TestResolveInstallAttempts:
def test_linux_cuda_does_not_fall_back_to_upstream_cpu(self, monkeypatch):
host = make_host(system = "Linux", machine = "x86_64", compute_caps = ["86"])
- release = make_release(
- [], release_tag = "llama-prebuilt-latest", upstream_tag = "b9000"
- )
+ release = make_release([], release_tag = "llama-prebuilt-latest", upstream_tag = "b9000")
checksums = make_checksums_with_source(
[],
release_tag = release.release_tag,
@@ -1758,9 +1695,7 @@ class TestResolveInstallAttempts:
)
mock_linux_runtime(monkeypatch, ["cuda12"])
- with pytest.raises(
- PrebuiltFallback, match = "no compatible published Linux CUDA bundle"
- ):
+ with pytest.raises(PrebuiltFallback, match = "no compatible published Linux CUDA bundle"):
resolve_install_attempts("latest", host, "unslothai/llama.cpp", "")
def test_windows_cpu_prefers_published_asset(self, monkeypatch):
@@ -1956,9 +1891,7 @@ class TestResolveInstallAttempts:
class TestResolveInstallReleasePlans:
- def test_latest_collects_multiple_older_release_plans_up_to_limit(
- self, monkeypatch
- ):
+ def test_latest_collects_multiple_older_release_plans_up_to_limit(self, monkeypatch):
host = make_host(
has_usable_nvidia = False,
has_physical_nvidia = False,
@@ -1994,9 +1927,7 @@ class TestResolveInstallReleasePlans:
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
"iter_resolved_published_releases",
- lambda requested_tag, published_repo, published_release_tag = "": iter(
- releases
- ),
+ lambda requested_tag, published_repo, published_release_tag = "": iter(releases),
)
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
@@ -2018,9 +1949,7 @@ class TestResolveInstallReleasePlans:
assert [plan.release_tag for plan in plans] == ["r3", "r2"]
assert [plan.llama_tag for plan in plans] == ["b9003", "b9002"]
- def test_latest_skips_non_installable_release_and_keeps_searching(
- self, monkeypatch
- ):
+ def test_latest_skips_non_installable_release_and_keeps_searching(self, monkeypatch):
host = make_host(
has_usable_nvidia = False,
has_physical_nvidia = False,
@@ -2048,9 +1977,7 @@ class TestResolveInstallReleasePlans:
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
"iter_resolved_published_releases",
- lambda requested_tag, published_repo, published_release_tag = "": iter(
- releases
- ),
+ lambda requested_tag, published_repo, published_release_tag = "": iter(releases),
)
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
@@ -2078,13 +2005,9 @@ class TestResolveInstallReleasePlans:
def test_malformed_release_fallback_env_uses_default(self, monkeypatch):
monkeypatch.setenv("UNSLOTH_LLAMA_MAX_PREBUILT_RELEASE_FALLBACKS", "not-an-int")
- assert (
- env_int("UNSLOTH_LLAMA_MAX_PREBUILT_RELEASE_FALLBACKS", 3, minimum = 1) == 3
- )
+ assert env_int("UNSLOTH_LLAMA_MAX_PREBUILT_RELEASE_FALLBACKS", 3, minimum = 1) == 3
- def test_import_with_malformed_release_fallback_env_does_not_crash(
- self, monkeypatch
- ):
+ def test_import_with_malformed_release_fallback_env_does_not_crash(self, monkeypatch):
monkeypatch.setenv("UNSLOTH_LLAMA_MAX_PREBUILT_RELEASE_FALLBACKS", "bad-value")
spec = importlib.util.spec_from_file_location(
"studio_install_llama_prebuilt_env_reload",
@@ -2108,7 +2031,11 @@ class TestResolveInstallReleasePlans:
class TestWindowsCudaAttempts:
TAG = "b8508"
- def _upstream(self, *runtime_versions, current_names: bool = False):
+ def _upstream(
+ self,
+ *runtime_versions,
+ current_names: bool = False,
+ ):
assets = {}
for rv in runtime_versions:
if current_names:
@@ -2350,23 +2277,14 @@ class TestPinnedBlackwellCudaFallback:
assert pin.runtime_sha256 and len(pin.runtime_sha256) == 64
def test_pin_offered_for_driver_13_2(self):
- assert (
- _pinned_windows_cuda_fallback(self._win_host((13, 2), ["120"]), [])
- is not None
- )
+ assert _pinned_windows_cuda_fallback(self._win_host((13, 2), ["120"]), []) is not None
def test_pin_offered_for_sm121_variant(self):
# sm_121 is Blackwell-family and also needs toolkit >= 12.8.
- assert (
- _pinned_windows_cuda_fallback(self._win_host((13, 1), ["121"]), [])
- is not None
- )
+ assert _pinned_windows_cuda_fallback(self._win_host((13, 1), ["121"]), []) is not None
def test_pin_uses_max_of_multi_gpu_caps(self):
- assert (
- _pinned_windows_cuda_fallback(self._win_host((13, 1), ["86", "120"]), [])
- is not None
- )
+ assert _pinned_windows_cuda_fallback(self._win_host((13, 1), ["86", "120"]), []) is not None
@pytest.mark.parametrize("sm", ["89", "90", "100"])
def test_pin_not_offered_to_non_blackwell(self, sm):
@@ -2377,16 +2295,11 @@ class TestPinnedBlackwellCudaFallback:
# b9360 is native sm_120a SASS (no JIT) and ships a cuda-13.1 cudart,
# both of which run on a 13.0 r580+ driver via CUDA minor-version
# compatibility. 13.0 is the mainstream Blackwell branch, so it must fire.
- assert (
- _pinned_windows_cuda_fallback(self._win_host((13, 0), ["120"]), [])
- is not None
- )
+ assert _pinned_windows_cuda_fallback(self._win_host((13, 0), ["120"]), []) is not None
def test_pin_not_offered_below_floor(self):
# 12.x predates Blackwell entirely; the pin stays dormant below 13.0.
- assert (
- _pinned_windows_cuda_fallback(self._win_host((12, 9), ["120"]), []) is None
- )
+ assert _pinned_windows_cuda_fallback(self._win_host((12, 9), ["120"]), []) is None
def test_pin_not_offered_without_driver(self):
assert _pinned_windows_cuda_fallback(self._win_host(None, ["120"]), []) is None
@@ -2460,10 +2373,7 @@ class TestPinnedBlackwellCudaFallback:
],
)
def test_attempt_covers_blackwell(self, minor, covers):
- assert (
- _windows_cuda_attempt_covers_blackwell(self._win_cuda_attempt(minor))
- is covers
- )
+ assert _windows_cuda_attempt_covers_blackwell(self._win_cuda_attempt(minor)) is covers
def test_attempt_covers_blackwell_ignores_non_cuda_kind(self):
cpu = AssetChoice(
@@ -2500,8 +2410,7 @@ class TestDirectUpstreamBlackwellPin:
return {
"tag_name": self.TAG,
"assets": [
- {"name": n, "browser_download_url": f"https://example.com/{n}"}
- for n in names
+ {"name": n, "browser_download_url": f"https://example.com/{n}"} for n in names
],
}
@@ -2521,15 +2430,9 @@ class TestDirectUpstreamBlackwellPin:
driver_cuda_version = (13, 1),
compute_caps = ["120"],
)
- plan = direct_upstream_release_plan(
- self._release(), host, UPSTREAM_REPO, "latest"
- )
+ plan = direct_upstream_release_plan(self._release(), host, UPSTREAM_REPO, "latest")
order = [(a.tag, a.runtime_line or a.install_kind) for a in plan.attempts]
- assert order == [
- ("b9360", "cuda13"),
- (self.TAG, "cuda12"),
- (self.TAG, "windows-cpu"),
- ]
+ assert order == [("b9360", "cuda13"), (self.TAG, "cuda12"), (self.TAG, "windows-cpu")]
assert plan.attempts[0].name == "llama-b9360-bin-win-cuda-13.1-x64.zip"
# Direct/upstream path stays unverified-by-manifest (no approved hashes).
assert plan.approved_checksums.artifacts == {}
@@ -2543,9 +2446,7 @@ class TestDirectUpstreamBlackwellPin:
driver_cuda_version = (13, 3),
compute_caps = ["120"],
)
- plan = direct_upstream_release_plan(
- self._release(), host, UPSTREAM_REPO, "latest"
- )
+ plan = direct_upstream_release_plan(self._release(), host, UPSTREAM_REPO, "latest")
assert "b9360" not in [a.tag for a in plan.attempts]
assert plan.attempts[0].tag == self.TAG
assert plan.attempts[0].runtime_line == "cuda13"
@@ -2581,9 +2482,7 @@ class TestPublishedWindowsCudaAttemptsDynamicMajor:
# the old hardcoded cuda12/cuda13 seed would never order it (the cuda14
# line would be skipped for want of a 14.x asset in the seed).
mock_windows_runtime(monkeypatch, ["cuda14", "cuda13", "cuda12"])
- release = self._release(
- [("14.0", "cuda14"), ("13.3", "cuda13"), ("12.4", "cuda12")]
- )
+ release = self._release([("14.0", "cuda14"), ("13.3", "cuda13"), ("12.4", "cuda12")])
host = make_host(
system = "Windows",
machine = "AMD64",
@@ -2809,18 +2708,14 @@ class TestResolveUpstreamAssetChoice:
def test_linux_x86_64_cpu(self, monkeypatch):
name = f"llama-{self.TAG}-bin-ubuntu-x64.tar.gz"
self._mock_github_assets(monkeypatch, {name: f"https://x/{name}"})
- host = make_host(
- has_usable_nvidia = False, nvidia_smi = None, has_physical_nvidia = False
- )
+ host = make_host(has_usable_nvidia = False, nvidia_smi = None, has_physical_nvidia = False)
result = resolve_upstream_asset_choice(host, self.TAG)
assert result.install_kind == "linux-cpu"
assert result.name == name
def test_linux_cpu_missing(self, monkeypatch):
self._mock_github_assets(monkeypatch, {})
- host = make_host(
- has_usable_nvidia = False, nvidia_smi = None, has_physical_nvidia = False
- )
+ host = make_host(has_usable_nvidia = False, nvidia_smi = None, has_physical_nvidia = False)
with pytest.raises(PrebuiltFallback, match = "Linux CPU"):
resolve_upstream_asset_choice(host, self.TAG)
@@ -2907,9 +2802,7 @@ class TestResolveUpstreamAssetChoice:
has_physical_nvidia = False,
has_usable_nvidia = False,
)
- with pytest.raises(
- PrebuiltFallback, match = "no prebuilt policy exists for Linux aarch64"
- ):
+ with pytest.raises(PrebuiltFallback, match = "no prebuilt policy exists for Linux aarch64"):
resolve_upstream_asset_choice(host, self.TAG)
def test_windows_usable_nvidia_delegates(self, monkeypatch):
@@ -3022,7 +2915,11 @@ class TestResolveSimpleMacosPin:
],
}
- def fake_iter(repo, published_release_tag = "", requested_tag = ""):
+ def fake_iter(
+ repo,
+ published_release_tag = "",
+ requested_tag = "",
+ ):
calls.append((repo, published_release_tag, requested_tag))
# Emulate the real iterator: a specific tag yields only that release.
if requested_tag and requested_tag != "latest":
@@ -3031,9 +2928,7 @@ class TestResolveSimpleMacosPin:
for tag in self.TAGS:
yield _release(tag)
- monkeypatch.setattr(
- INSTALL_LLAMA_PREBUILT, "iter_release_payloads_by_time", fake_iter
- )
+ monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "iter_release_payloads_by_time", fake_iter)
return calls
def test_pre26_host_pins_b9415_without_walkback(self, monkeypatch):
@@ -3081,14 +2976,10 @@ class TestLinuxArm64ForkFallsBackToSource:
def _boom(*_a, **_k):
raise AssertionError("iterator must not run for arm64 fork hosts")
- monkeypatch.setattr(
- INSTALL_LLAMA_PREBUILT, "iter_release_payloads_by_time", _boom
- )
+ monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "iter_release_payloads_by_time", _boom)
host = make_host(system = "Linux", machine = "aarch64")
with pytest.raises(PrebuiltFallback, match = "linux-x64 prebuilts"):
- resolve_simple_install_release_plans(
- "latest", host, "unslothai/llama.cpp", ""
- )
+ resolve_simple_install_release_plans("latest", host, "unslothai/llama.cpp", "")
def test_x86_64_fork_is_not_blocked_by_the_arch_guard(self, monkeypatch):
# x64 host must pass the guard and reach the iterator (here empty, so it
@@ -3100,9 +2991,7 @@ class TestLinuxArm64ForkFallsBackToSource:
)
host = make_host(system = "Linux", machine = "x86_64")
with pytest.raises(PrebuiltFallback) as exc:
- resolve_simple_install_release_plans(
- "latest", host, "unslothai/llama.cpp", ""
- )
+ resolve_simple_install_release_plans("latest", host, "unslothai/llama.cpp", "")
assert "linux-x64 prebuilts" not in str(exc.value)
def test_arm64_cpu_on_ggml_org_is_not_blocked(self, monkeypatch):
@@ -3123,9 +3012,7 @@ class TestLinuxArm64ForkFallsBackToSource:
has_usable_nvidia = False,
)
with pytest.raises(PrebuiltFallback) as exc:
- resolve_simple_install_release_plans(
- "latest", host, "ggml-org/llama.cpp", ""
- )
+ resolve_simple_install_release_plans("latest", host, "ggml-org/llama.cpp", "")
assert "linux-x64 prebuilts" not in str(exc.value)
@@ -3212,9 +3099,7 @@ class TestCpuFallback:
has_physical_nvidia = False,
has_usable_nvidia = False,
)
- plan = direct_upstream_release_plan(
- release, cpu_host, "ggml-org/llama.cpp", "latest"
- )
+ plan = direct_upstream_release_plan(release, cpu_host, "ggml-org/llama.cpp", "latest")
assert plan.attempts[0].install_kind == "linux-arm64"
assert plan.attempts[0].name == f"llama-{tag}-bin-ubuntu-arm64.tar.gz"
diff --git a/tests/studio/load_freeze/llama_server_shim.py b/tests/studio/load_freeze/llama_server_shim.py
index 1166c7521d..bb9e119820 100644
--- a/tests/studio/load_freeze/llama_server_shim.py
+++ b/tests/studio/load_freeze/llama_server_shim.py
@@ -41,7 +41,11 @@ class _Handler(BaseHTTPRequestHandler):
self.wfile.write(payload)
def _send_raw(
- self, status: int, body: bytes, *, content_type: str = "application/json"
+ self,
+ status: int,
+ body: bytes,
+ *,
+ content_type: str = "application/json",
) -> None:
self.send_response(status)
self.send_header("Content-Type", content_type)
@@ -119,9 +123,7 @@ class _Handler(BaseHTTPRequestHandler):
self._send_raw(srv.config.detok_status, srv.config.detok_body)
return
tids = body.get("tokens") or []
- content = "".join(
- srv.config.detok_map.get(int(t), f"") for t in tids
- )
+ content = "".join(srv.config.detok_map.get(int(t), f"") for t in tids)
self._send_json(srv.config.detok_status, {"content": content})
return
if path == "/completion":
@@ -224,9 +226,7 @@ class FakeLlamaServer:
def start(self) -> "FakeLlamaServer":
# port=0 lets ThreadingHTTPServer pick a free port atomically
# (avoids find-port-then-bind race); read back via server_address[1].
- self._server = FakeLlamaServer._Server(
- (self.host, self._requested_port), _Handler
- )
+ self._server = FakeLlamaServer._Server((self.host, self._requested_port), _Handler)
self._server.config = self.config
bound_port = self._server.server_address[1]
self._thread = threading.Thread(
diff --git a/tests/studio/load_freeze/test_load_orchestrator.py b/tests/studio/load_freeze/test_load_orchestrator.py
index 8d32932d13..b76c4c361b 100644
--- a/tests/studio/load_freeze/test_load_orchestrator.py
+++ b/tests/studio/load_freeze/test_load_orchestrator.py
@@ -3,7 +3,7 @@
Covers:
1. Behavioural canary (the bug class) — 2 tests
2. Behavioural fix-validation — 1 test
- 3. Functional equivalence (sync == to_thread) — 5 tests, one per codec branch
+ 3. Functional equivalence (sync == to_thread) — 6 tests, one per codec branch
4. Failure modes (HTTP 500, malformed JSON,
connection reset, unreachable, not-loaded) — 5 tests
5. Stress (50 concurrent probes / 100 healths) — 2 tests
@@ -103,14 +103,18 @@ def _free_port() -> int:
class _UvicornServerThread:
- def __init__(self, app, *, host: str = "127.0.0.1", port: int) -> None:
+ def __init__(
+ self,
+ app,
+ *,
+ host: str = "127.0.0.1",
+ port: int,
+ ) -> None:
import uvicorn
self.host = host
self.port = port
- cfg = uvicorn.Config(
- app, host = host, port = port, log_level = "warning", access_log = False
- )
+ cfg = uvicorn.Config(app, host = host, port = port, log_level = "warning", access_log = False)
self._server = uvicorn.Server(cfg)
self._server.install_signal_handlers = lambda: None # type: ignore[assignment]
self._thread: threading.Thread | None = None
@@ -169,7 +173,12 @@ def _build_app(backend, *, wrap_in_thread: bool):
return app
-def _drive_concurrent_probe_and_health(base_url, *, n_health = 12, gap = 0.05):
+def _drive_concurrent_probe_and_health(
+ base_url,
+ *,
+ n_health = 12,
+ gap = 0.05,
+):
elapsed = -1.0
latencies: list[float] = []
@@ -211,9 +220,7 @@ def test_buggy_route_blocks_event_loop():
app = _build_app(backend, wrap_in_thread = False)
port = _free_port()
with _UvicornServerThread(app, port = port) as uv:
- max_lat, probe_t, _ = _drive_concurrent_probe_and_health(
- f"http://127.0.0.1:{uv.port}"
- )
+ max_lat, probe_t, _ = _drive_concurrent_probe_and_health(f"http://127.0.0.1:{uv.port}")
assert probe_t >= 0.5
assert max_lat >= 0.4, f"expected >=0.4s stall, got {max_lat:.3f}s"
@@ -254,6 +261,7 @@ def shim_no_match():
"<|audio_eos|>": [0, 1],
"<|startoftranscript|>": [0, 1],
"": [0, 1],
+ "<|audio|>": [0, 1],
"<|bicodec_semantic_0|>": [0, 1],
"<|bicodec_global_0|>": [0, 1],
"<|c1_0|>": [0, 1],
@@ -314,6 +322,28 @@ def test_functional_equivalence_whisper_match():
assert sync_result == threaded
+def test_functional_equivalence_audio_vlm_match():
+ # audio_vlm: snac/csm/whisper fail first, then the Gemma 4 <|audio|>
+ # probe tokenises to a single token. #6000 added this arm alongside
+ # Gemma 3n's ; keep at 2 tokens so
+ # it is specifically the new <|audio|> arm that triggers the match.
+ with FakeLlamaServer(
+ detok_map = {128258: "non-snac", 128259: "non-snac"},
+ tok_response_map = {
+ "<|AUDIO|>": [0, 1], # csm fails (>1 token)
+ "<|audio_eos|>": [0, 1],
+ "<|startoftranscript|>": [0, 1], # whisper fails
+ "": [0, 1], # Gemma 3n arm fails ...
+ "<|audio|>": [0], # ... Gemma 4 arm matches (#6000)
+ },
+ ) as srv:
+ backend = _make_backend(srv.port)
+ sync_result = backend.detect_audio_type()
+ threaded = asyncio.run(asyncio.to_thread(backend.detect_audio_type))
+ assert sync_result == "audio_vlm"
+ assert sync_result == threaded
+
+
def test_functional_equivalence_bicodec_match():
# bicodec: snac/csm/whisper/audio_vlm all fail first, then both
# bicodec_semantic_0 and bicodec_global_0 are single tokens.
@@ -324,6 +354,7 @@ def test_functional_equivalence_bicodec_match():
"<|audio_eos|>": [0, 1],
"<|startoftranscript|>": [0, 1],
"": [0, 1],
+ "<|audio|>": [0, 1],
"<|bicodec_semantic_0|>": [0],
"<|bicodec_global_0|>": [0],
},
@@ -418,9 +449,7 @@ def test_50_concurrent_probes_complete_without_deadlock():
with ThreadPoolExecutor(max_workers = 50) as pool:
futs = [
pool.submit(
- lambda: httpx.get(
- f"http://127.0.0.1:{uv.port}/probe", timeout = 30.0
- )
+ lambda: httpx.get(f"http://127.0.0.1:{uv.port}/probe", timeout = 30.0)
)
for _ in range(50)
]
@@ -636,7 +665,6 @@ def test_response_shape_matches_pre_fix_for_no_match():
bodies for the no-match scenario (the dominant code path in
practice for non-audio models)."""
import json as _json
-
with FakeLlamaServer(
detok_map = {128258: "abc", 128259: "def"},
tok_response_map = {
@@ -644,6 +672,7 @@ def test_response_shape_matches_pre_fix_for_no_match():
"<|audio_eos|>": [0, 1],
"<|startoftranscript|>": [0, 1],
"": [0, 1],
+ "<|audio|>": [0, 1],
"<|bicodec_semantic_0|>": [0, 1],
"<|bicodec_global_0|>": [0, 1],
"<|c1_0|>": [0, 1],
diff --git a/tests/studio/playwright_chat_ime_i18n.py b/tests/studio/playwright_chat_ime_i18n.py
index efcd048b44..828187d462 100644
--- a/tests/studio/playwright_chat_ime_i18n.py
+++ b/tests/studio/playwright_chat_ime_i18n.py
@@ -246,8 +246,7 @@ with sync_playwright() as p:
dir_attr = composer.evaluate("(el) => el.getAttribute('dir')")
if dir_attr != "auto":
soft_fail(
- f'composer is missing dir="auto" (got {dir_attr!r}); RTL '
- "languages will render LTR."
+ f'composer is missing dir="auto" (got {dir_attr!r}); RTL ' "languages will render LTR."
)
else:
info('composer dir="auto" present')
@@ -258,9 +257,7 @@ with sync_playwright() as p:
_thread_src = (
_repo_root / "studio/frontend/src/components/assistant-ui/thread.tsx"
).read_text()
- _shared_src = (
- _repo_root / "studio/frontend/src/features/chat/shared-composer.tsx"
- ).read_text()
+ _shared_src = (_repo_root / "studio/frontend/src/features/chat/shared-composer.tsx").read_text()
_edit_idx = _thread_src.find("aui-edit-composer-input")
if _edit_idx == -1 or 'dir="auto"' not in _thread_src[_edit_idx : _edit_idx + 600]:
soft_fail('edit composer source is missing dir="auto"')
@@ -269,8 +266,7 @@ with sync_playwright() as p:
_compare_idx = _shared_src.find("Send to both models")
if (
_compare_idx == -1
- or 'dir="auto"'
- not in _shared_src[max(_compare_idx - 400, 0) : _compare_idx + 400]
+ or 'dir="auto"' not in _shared_src[max(_compare_idx - 400, 0) : _compare_idx + 400]
):
soft_fail('compare composer source is missing dir="auto"')
else:
@@ -484,9 +480,7 @@ with sync_playwright() as p:
# handleSubmit / blockSend guards keep refusing. The Send button stays
# visually enabled (watchdog has already cleared the React state); the
# refusal happens at form.requestSubmit() time, not at the button.
- step(
- "BUG REPRO: keydown re-pin after watchdog cleared composing (issue #5546 follow-up)"
- )
+ step("BUG REPRO: keydown re-pin after watchdog cleared composing (issue #5546 follow-up)")
clear()
composer.click()
composer.evaluate(
@@ -533,9 +527,7 @@ with sync_playwright() as p:
"Form submitted after an IME keydown -- preedit text leaked "
"through the watchdog gap (#5546 follow-up regression)."
)
- info(
- f"Form submit refused after IME keydown; textarea retained {submit_probe.get('after')!r}"
- )
+ info(f"Form submit refused after IME keydown; textarea retained {submit_probe.get('after')!r}")
shoot("06c-keydown-repin")
info("keydown re-pin gate PASS")
clear()
diff --git a/tests/studio/playwright_chat_ui.py b/tests/studio/playwright_chat_ui.py
index b1279e64b9..dc62194be8 100644
--- a/tests/studio/playwright_chat_ui.py
+++ b/tests/studio/playwright_chat_ui.py
@@ -141,10 +141,7 @@ def expected_default_model():
for node in tree.body:
if not isinstance(node, ast.Assign):
continue
- if not any(
- isinstance(t, ast.Name) and t.id == "DEFAULT_MODELS_GGUF"
- for t in node.targets
- ):
+ if not any(isinstance(t, ast.Name) and t.id == "DEFAULT_MODELS_GGUF" for t in node.targets):
continue
try:
models = ast.literal_eval(node.value)
@@ -321,9 +318,7 @@ with sync_playwright() as p:
form_err: Exception | None = None
for _form_attempt in range(3):
try:
- page.goto(
- f"{BASE}/change-password", wait_until = "domcontentloaded", timeout = 60_000
- )
+ page.goto(f"{BASE}/change-password", wait_until = "domcontentloaded", timeout = 60_000)
try:
page.wait_for_load_state("networkidle", timeout = 30_000)
except Exception:
@@ -382,9 +377,7 @@ with sync_playwright() as p:
flush = True,
)
if page_errors:
- print(
- f"[ui] first pageerror: {page_errors[0][:200]!r}", flush = True
- )
+ print(f"[ui] first pageerror: {page_errors[0][:200]!r}", flush = True)
try:
shoot(f"01-change-password-attempt-{_form_attempt + 1}-fail")
except Exception:
@@ -458,9 +451,7 @@ with sync_playwright() as p:
flush = True,
)
if page_errors:
- print(
- f"[ui] first pageerror: {page_errors[0][:200]!r}", flush = True
- )
+ print(f"[ui] first pageerror: {page_errors[0][:200]!r}", flush = True)
try:
shoot(f"03-composer-wait-attempt-{_attempt + 1}-fail")
except Exception:
@@ -557,9 +548,7 @@ with sync_playwright() as p:
try:
sel_text = (selector_btn.text_content(timeout = 2_000) or "").strip()
except Exception as _sel_err:
- info(
- f"WARN: model-selector probe skipped: {type(_sel_err).__name__}: {_sel_err}"
- )
+ info(f"WARN: model-selector probe skipped: {type(_sel_err).__name__}: {_sel_err}")
if sel_text:
info(f"model selector button text: {sel_text!r}")
shoot("03b-default-model-button")
@@ -595,10 +584,7 @@ with sync_playwright() as p:
if load_resp.get("error"):
fail(f"/api/inference/load wedged: {load_resp['error']!r}")
if load_resp["status"] != 200:
- fail(
- f"/api/inference/load returned {load_resp['status']}: "
- f"{load_resp.get('body')!r}"
- )
+ fail(f"/api/inference/load returned {load_resp['status']}: " f"{load_resp.get('body')!r}")
info(f"loaded model: {(load_resp['body'] or {}).get('display_name')}")
# Studio caches the per-context model state in zustand; reload
@@ -845,8 +831,7 @@ with sync_playwright() as p:
# Look for either "Disable X" or "Enable X" -- whichever
# is currently rendered.
toggle = page.locator(
- f'button[aria-label="Disable {feature}"], '
- f'button[aria-label="Enable {feature}"]'
+ f'button[aria-label="Disable {feature}"], ' f'button[aria-label="Enable {feature}"]'
).first
if toggle.count() == 0:
info(f"toggle '{feature}' not present on this layout")
@@ -862,8 +847,7 @@ with sync_playwright() as p:
page.wait_for_timeout(200)
after = (
page.locator(
- f'button[aria-label="Disable {feature}"], '
- f'button[aria-label="Enable {feature}"]'
+ f'button[aria-label="Disable {feature}"], ' f'button[aria-label="Enable {feature}"]'
).first.get_attribute("aria-label")
or ""
)
@@ -874,8 +858,7 @@ with sync_playwright() as p:
# Flip back so test state is unchanged.
try:
page.locator(
- f'button[aria-label="Disable {feature}"], '
- f'button[aria-label="Enable {feature}"]'
+ f'button[aria-label="Disable {feature}"], ' f'button[aria-label="Enable {feature}"]'
).first.click()
except Exception:
pass
@@ -968,8 +951,7 @@ with sync_playwright() as p:
except Exception as exc:
if attempt == 1:
soft_fail(
- f"theme cycle {cycle + 1}: account-menu click failed "
- f"({exc!r})"
+ f"theme cycle {cycle + 1}: account-menu click failed " f"({exc!r})"
)
continue
try:
@@ -1020,8 +1002,7 @@ with sync_playwright() as p:
if click_err is not None:
page.keyboard.press("Escape")
soft_fail(
- f"theme cycle {cycle + 1}: theme menuitem click failed "
- f"({click_err!r})"
+ f"theme cycle {cycle + 1}: theme menuitem click failed " f"({click_err!r})"
)
break
# Settle. The ".dark" class on is the ground
@@ -1078,9 +1059,7 @@ with sync_playwright() as p:
# progressively more permissive locators so the test stays
# green on both platforms.
candidates = [
- page.get_by_role(
- "button", name = re.compile(rf"^\s*{label}\s*$", re.I)
- ).first,
+ page.get_by_role("button", name = re.compile(rf"^\s*{label}\s*$", re.I)).first,
page.locator(f'button:has-text("{label}")').first,
page.locator(f'a:has-text("{label}")').first,
page.locator(f'[data-sidebar="menu-button"]:has-text("{label}")').first,
@@ -1116,15 +1095,11 @@ with sync_playwright() as p:
click_nav("New Chat", r"/chat")
shoot("11-new-chat")
# Compare moved into the composer + menu (Tools and attachments).
- plus_btn = page.get_by_role(
- "button", name = re.compile(r"Tools and attachments", re.I)
- ).first
+ plus_btn = page.get_by_role("button", name = re.compile(r"Tools and attachments", re.I)).first
if plus_btn.count() > 0:
plus_btn.click(force = True)
page.wait_for_timeout(400)
- compare_item = page.get_by_role(
- "menuitem", name = re.compile(r"Compare chat", re.I)
- ).first
+ compare_item = page.get_by_role("menuitem", name = re.compile(r"Compare chat", re.I)).first
if compare_item.count() > 0:
compare_item.click(force = True)
page.wait_for_timeout(800)
@@ -1159,9 +1134,7 @@ with sync_playwright() as p:
step("Developer (API) tab via account menu")
acct.click()
page.wait_for_timeout(400)
- dev = page.get_by_role(
- "menuitem", name = re.compile(r"developer|api", re.I)
- ).first
+ dev = page.get_by_role("menuitem", name = re.compile(r"developer|api", re.I)).first
if dev.count() > 0:
dev.click()
page.wait_for_timeout(800)
@@ -1178,9 +1151,7 @@ with sync_playwright() as p:
re.compile(r"api keys|developer", re.I),
).first
if keys_section.count() > 0:
- info(
- f"OK API tab text: {(keys_section.text_content() or '').strip()[:80]!r}"
- )
+ info(f"OK API tab text: {(keys_section.text_content() or '').strip()[:80]!r}")
# Close dialog with Escape.
page.keyboard.press("Escape")
page.wait_for_timeout(300)
@@ -1198,9 +1169,7 @@ with sync_playwright() as p:
page.wait_for_timeout(1500)
# Recipe cards are rendered as or button elements; count
# all clickable headings under main + screenshot.
- headings = page.locator(
- "main h2, main h3, [data-recipe], a[href*='/data-recipes/']"
- )
+ headings = page.locator("main h2, main h3, [data-recipe], a[href*='/data-recipes/']")
n_cards = headings.count()
info(f"Recipes route headings/cards: {n_cards}")
shoot("15b-recipes-cards")
@@ -1289,10 +1258,7 @@ with sync_playwright() as p:
info(f"recent-thread click {i} failed: {_click_err!s}")
continue
if not clicked_recent:
- soft_fail(
- f"no Recents entry was clickable within 30s deadline "
- f"(n_threads={n_threads})"
- )
+ soft_fail(f"no Recents entry was clickable within 30s deadline " f"(n_threads={n_threads})")
# Back to chat.
page.goto(f"{BASE}/chat")
composer = page.locator('textarea[aria-label="Message input"]')
diff --git a/tests/studio/playwright_extra_ui.py b/tests/studio/playwright_extra_ui.py
index 0ac9f67a7e..20c7bda87c 100644
--- a/tests/studio/playwright_extra_ui.py
+++ b/tests/studio/playwright_extra_ui.py
@@ -170,9 +170,7 @@ with sync_playwright() as p:
form_err: Exception | None = None
for _form_attempt in range(3):
try:
- page.goto(
- f"{BASE}/change-password", wait_until = "domcontentloaded", timeout = 60_000
- )
+ page.goto(f"{BASE}/change-password", wait_until = "domcontentloaded", timeout = 60_000)
try:
page.wait_for_load_state("networkidle", timeout = 30_000)
except Exception:
@@ -329,15 +327,11 @@ with sync_playwright() as p:
step("Compare tab: send to two panes")
# Compare moved into the composer + menu (Tools and attachments).
compare_opened = False
- plus_btn = page.get_by_role(
- "button", name = re.compile(r"Tools and attachments", re.I)
- ).first
+ plus_btn = page.get_by_role("button", name = re.compile(r"Tools and attachments", re.I)).first
if plus_btn.count() > 0:
plus_btn.click(force = True)
page.wait_for_timeout(400)
- compare_item = page.get_by_role(
- "menuitem", name = re.compile(r"Compare chat", re.I)
- ).first
+ compare_item = page.get_by_role("menuitem", name = re.compile(r"Compare chat", re.I)).first
if compare_item.count() > 0:
compare_item.click(force = True)
compare_opened = True
@@ -416,9 +410,7 @@ with sync_playwright() as p:
arg = ok_count_before + 4,
timeout = 60_000,
)
- info(
- "OK Compare: 4 total new assistant bubbles after second prompt"
- )
+ info("OK Compare: 4 total new assistant bubbles after second prompt")
except Exception as exc:
runtime_warn(
f"Compare: 4 bubbles didn't appear (panes likely "
@@ -439,9 +431,7 @@ with sync_playwright() as p:
page.wait_for_timeout(1500)
shoot("05-recipes-list")
# Template cards render as