| import os
|
| import shutil
|
| from pathlib import Path
|
|
|
| from safetensors import safe_open
|
|
|
|
|
| def _ensure_dir(path: Path):
|
| path.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
| def _move_with_collision_handling(src: Path, dst_dir: Path):
|
| _ensure_dir(dst_dir)
|
| target = dst_dir / src.name
|
| if target.exists():
|
| stem, suffix = os.path.splitext(src.name)
|
| idx = 1
|
| while True:
|
| candidate = dst_dir / f"{stem}_{idx}{suffix}"
|
| if not candidate.exists():
|
| target = candidate
|
| break
|
| idx += 1
|
| shutil.move(str(src), str(target))
|
|
|
|
|
| def _classify_wan_lora(file_path: Path, subfolder_hint: str | None = None) -> str:
|
| if subfolder_hint in {"1.3B"}:
|
| return "wan_1.3B"
|
| if subfolder_hint in {"5B"}:
|
| return "wan_5B"
|
| if subfolder_hint in {"14B"}:
|
| return "wan"
|
|
|
| candidate_tokens = [
|
| "to_q",
|
| "to_k",
|
| "to_v",
|
| "proj_in",
|
| "proj_out",
|
| "ff.net",
|
| "ffn",
|
| "transformer",
|
| "attn",
|
| "gate_proj",
|
| "up_proj",
|
| "down_proj",
|
| "lora_down",
|
| "lora_up",
|
| ]
|
| try:
|
| with safe_open(file_path, framework="pt", device="cpu") as f:
|
| keys = f.keys()
|
| if not keys:
|
| return "wan"
|
| max_dim = 0
|
|
|
| for key in keys:
|
| k_lower = key.lower()
|
| if not any(token in k_lower for token in candidate_tokens):
|
| continue
|
| tensor = f.get_tensor(key)
|
| max_dim = max(max_dim, max(tensor.shape) if tensor.ndim > 0 else 0)
|
| if max_dim == 0:
|
| tensor = f.get_tensor(keys[0])
|
| max_dim = max(tensor.shape) if tensor.ndim > 0 else 0
|
| if max_dim >= 5000:
|
| return "wan"
|
| if max_dim >= 3000:
|
| return "wan_5B"
|
| return "wan_1.3B"
|
| except Exception:
|
| return "wan"
|
|
|
|
|
| def _move_dir_contents(src_dir: Path, dst_dir: Path):
|
| if not src_dir.exists():
|
| return
|
| for item in src_dir.iterdir():
|
| _move_with_collision_handling(item, dst_dir)
|
| try:
|
| src_dir.rmdir()
|
| except OSError:
|
| pass
|
|
|
|
|
| def migrate_loras_layout(root_dir: Path | str | None = None):
|
| """
|
| Reorganize lora folders into a single 'loras' tree.
|
| Migration is skipped if root/loras_i2v is already gone.
|
| """
|
| root = Path(root_dir or ".").resolve()
|
| marker = root / "loras_i2v"
|
| if not marker.exists():
|
| return
|
|
|
| loras_root = root / "loras"
|
| _ensure_dir(loras_root)
|
|
|
|
|
| for entry in root.iterdir():
|
| if not entry.is_dir():
|
| continue
|
| if entry.name in {"loras", "loras_i2v"}:
|
| continue
|
| if entry.name.startswith("loras_"):
|
| target_name = entry.name[len("loras_") :]
|
| _move_dir_contents(entry, loras_root / target_name)
|
|
|
|
|
| _move_dir_contents(marker, loras_root / "wan_i2v")
|
|
|
|
|
| wan_dir = loras_root / "wan"
|
| wan_1_3b_dir = loras_root / "wan_1.3B"
|
| wan_5b_dir = loras_root / "wan_5B"
|
| wan_i2v_dir = loras_root / "wan_i2v"
|
| optional_hints = {"1.3B", "5B", "14B"}
|
| moved_wan_by_signature = {}
|
| for legacy_name, target in [
|
| ("14B", wan_dir),
|
| ("1.3B", wan_1_3b_dir),
|
| ("5B", wan_5b_dir),
|
| ]:
|
| _move_dir_contents(loras_root / legacy_name, target)
|
|
|
|
|
| for item in list(loras_root.iterdir()):
|
| if item.is_dir() and item.name.startswith("wan"):
|
| continue
|
| if item.is_dir():
|
|
|
| continue
|
| if not item.is_file():
|
| continue
|
| subfolder_hint = item.parent.name
|
| used_hint = subfolder_hint in optional_hints
|
| target_bucket = _classify_wan_lora(item, subfolder_hint=subfolder_hint if used_hint else None)
|
| target_dir = {"wan": wan_dir, "wan_1.3B": wan_1_3b_dir, "wan_5B": wan_5b_dir}.get(
|
| target_bucket, wan_dir
|
| )
|
| _move_with_collision_handling(item, target_dir)
|
| if target_bucket.startswith("wan") and not used_hint:
|
| moved_wan_by_signature[item.name] = target_dir
|
|
|
|
|
| for item in list(loras_root.iterdir()):
|
| if item.is_file():
|
| _move_with_collision_handling(item, wan_dir)
|
|
|
|
|
| for lset_file in loras_root.glob("*.lset"):
|
| try:
|
| content = lset_file.read_text(errors="ignore")
|
| except Exception:
|
| continue
|
| for lora_name, dest_dir in moved_wan_by_signature.items():
|
| if lora_name in content:
|
| _move_with_collision_handling(lset_file, dest_dir)
|
| break
|
|
|
| for folder in [wan_dir, wan_1_3b_dir, wan_5b_dir, wan_i2v_dir]:
|
| _ensure_dir(folder)
|
|
|