Show more information about LoRAs when loading loras in generate page

This commit is contained in:
Jaret Burkett committed 2026-09-17 09:35:43 -06:00
1 parent ef8110cef7
commit 1db6083645
2 files changed
+46 -8

No files matched your search

+45 -7
View File
@@ -152,6 +152,9 @@ class InferenceLoRA:
self.name = name or os.path.splitext(os.path.basename(path))[0]
self.entries: List[LoRAEntry] = []
self.unmatched: List[str] = []
self.num_keys = 0 # tensors in the file
self.num_skipped_keys = 0 # keys with no recognized lora/diff/lokr suffix
self.convert_error: Optional[str] = None
self._hooks: List = []
# ---- loading / key resolution ----
@@ -163,18 +166,23 @@ class InferenceLoRA:
# self.path stays as given: it is the spec identity the engine keys its
# loaded stack on.
self.local_path = resolve_lora_file(self.path, status_fn=status_fn)
if status_fn:
status_fn(f"Loading LoRA {self.name} from {self.local_path}")
sd = load_file(self.local_path)
self.num_keys = len(sd)
convert = getattr(holder, "convert_lora_weights_before_load", None)
if callable(convert):
try:
sd = convert(sd)
except Exception:
pass
except Exception as e:
self.convert_error = f"{type(e).__name__}: {e}"
roots = self._roots(holder)
grouped: Dict[Tuple[int, str], dict] = {}
self.num_skipped_keys = 0
for key, tensor in sd.items():
base, part = self._split(key)
if part is None:
self.num_skipped_keys += 1
continue
grouped.setdefault(base, {})[part] = tensor
self.entries = []
@@ -378,6 +386,35 @@ class InferenceLoRA:
m_marker = getattr(self, "_marker", None)
return merged
def report(self, max_unmatched: Optional[int] = None) -> str:
"""Multi-line load report: file, key counts, matched/unmatched modules, unmatched names."""
n_groups = len(self.entries) + len(self.unmatched)
lines = [
f"LoRA {self.name}: {self.local_path or self.path}",
f" {self.num_keys} keys in file -> {n_groups} modules: {len(self.entries)} matched, {len(self.unmatched)} unmatched"
+ (f", {self.num_skipped_keys} keys ignored (unrecognized suffix)" if self.num_skipped_keys else ""),
]
if self.convert_error:
lines.append(f" convert_lora_weights_before_load failed, using raw keys: {self.convert_error}")
shown = self.unmatched if max_unmatched is None else self.unmatched[:max_unmatched]
for u in shown:
lines.append(f" unmatched: {u}")
if len(shown) < len(self.unmatched):
lines.append(f" ... {len(self.unmatched) - len(shown)} more unmatched")
return "\n".join(lines)
def report_line(self, max_unmatched: int = 5) -> str:
"""One-line form of report() for status strips."""
n_groups = len(self.entries) + len(self.unmatched)
line = f"LoRA {self.name}: {len(self.entries)}/{n_groups} modules matched ({self.num_keys} keys)"
if self.unmatched:
shown = ", ".join(self.unmatched[:max_unmatched])
more = len(self.unmatched) - max_unmatched
line += f"; {len(self.unmatched)} unmatched: {shown}" + (f" +{more} more" if more > 0 else "")
if self.convert_error:
line += f"; convert failed: {self.convert_error}"
return line
def summary(self) -> dict:
return {
"name": self.name,
@@ -404,13 +441,14 @@ class LoRAStack:
lora = InferenceLoRA(spec["path"], spec.get("strength", 1.0), spec.get("name")).load(
self.holder, status_fn=status_fn
)
print(f"[AITK] {lora.report()}", flush=True)
if status_fn:
status_fn(
f"LoRA {lora.name}: {len(lora.entries)} modules"
+ (f", {len(lora.unmatched)} unmatched" if lora.unmatched else "")
)
status_fn(lora.report_line())
if not lora.entries:
raise ValueError(f"LoRA {lora.name} matched no modules of this model (unmatched: {lora.unmatched[:3]})")
raise ValueError(
f"LoRA {lora.name} matched no modules of this model "
f"({lora.num_keys} keys, {len(lora.unmatched)} unmatched; see the engine job log for the list)"
)
self.loras.append(lora)
return self