173 lines
6.7 KiB
Python
173 lines
6.7 KiB
Python
import gc
|
|
import os
|
|
import traceback
|
|
from typing import List, Union
|
|
import requests
|
|
import torch
|
|
import timm
|
|
from unifiedefficientloader import UnifiedSafetensorsLoader
|
|
from tqdm import tqdm
|
|
import comfy.model_management as mm
|
|
import comfy.utils
|
|
|
|
from ..helpers.cache import CacheCleanupMethod, ComfyCache
|
|
from ..helpers.config import ComfyExtensionConfig
|
|
from ..helpers.logger import ComfyLogger
|
|
from ..helpers.metaclasses import Singleton
|
|
|
|
|
|
class V2GatedHead(torch.nn.Module):
|
|
def __init__(self,
|
|
num_features: int,
|
|
num_classes: int):
|
|
super().__init__()
|
|
self.num_classes = num_classes
|
|
self.linear = torch.nn.Linear(num_features, num_classes * 2)
|
|
self.act = torch.nn.Sigmoid()
|
|
self.gate = torch.nn.Sigmoid()
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
x = self.linear(x)
|
|
x = self.act(x[:, :self.num_classes]) * self.gate(x[:, self.num_classes:])
|
|
return x
|
|
|
|
|
|
class JtpModelManager(metaclass=Singleton):
|
|
"""
|
|
The RedRocket JTP Model Manager class is a singleton class that manages the loading, unloading, downloading, and installation of JTP Vision Transformer models.
|
|
"""
|
|
def __init__(self, model_basepath: str) -> None:
|
|
self.model_basepath = model_basepath
|
|
ComfyCache.set_max_size('model', 1)
|
|
ComfyCache.set_cachemethod('model', CacheCleanupMethod.ROUND_ROBIN)
|
|
|
|
def __del__(self) -> None:
|
|
ComfyCache.flush('model')
|
|
gc.collect()
|
|
|
|
@classmethod
|
|
def is_loaded(cls, model_name: str) -> bool:
|
|
"""
|
|
Check if a RedRocket JTP Vision Transformer model is loaded into memory
|
|
"""
|
|
return ComfyCache.get(f'model.{model_name}') is not None and ComfyCache.get(f'model.{model_name}.model') is not None
|
|
|
|
@classmethod
|
|
def load(cls, model_name: str, version: int = 1, device: torch.device = torch.device('cpu')) -> bool:
|
|
"""
|
|
Load a RedRocket JTP Vision Transformer model into memory
|
|
"""
|
|
model_path = os.path.join(cls().model_basepath, f"{model_name}.safetensors")
|
|
if cls.is_loaded(model_name):
|
|
ComfyLogger().log(message=f"Model {model_name} already loaded", type="WARNING", always=True)
|
|
return True
|
|
if not os.path.exists(model_path):
|
|
ComfyLogger().log(message=f"Model {model_name} not found in path: {model_path}",type= "ERROR", always=True)
|
|
return False
|
|
|
|
ComfyLogger().log(message=f"Loading model {model_name} (version: {version}) from {model_path}...", type="INFO", always=True)
|
|
model: torch.nn.Module = timm.create_model("vit_so400m_patch14_siglip_384.webli", pretrained=False, num_classes=9083)
|
|
if f"{version}" == "2":
|
|
model.head = V2GatedHead(min(model.head.weight.shape), 9083)
|
|
with UnifiedSafetensorsLoader(model_path, low_memory=True) as loader:
|
|
sd = {key: loader.get_tensor(key) for key in loader.keys()}
|
|
model.load_state_dict(sd)
|
|
model.to(device="cpu", dtype=torch.float32)
|
|
model.eval()
|
|
ComfyCache.set(f'model.{model_name}', {
|
|
"model": model,
|
|
"version": version,
|
|
"device": torch.device('cpu')
|
|
})
|
|
ComfyLogger().log(message=f"Model {model_name} loaded successfully on CPU", type="INFO", always=True)
|
|
return True
|
|
|
|
@classmethod
|
|
def unload(cls, model_name: str) -> bool:
|
|
"""
|
|
Unload a RedRocket JTP Vision Transformer model from memory
|
|
"""
|
|
if not cls.is_loaded(model_name):
|
|
ComfyLogger().log(message=f"Model {model_name} not loaded, nothing to do here", type="WARNING", always=True)
|
|
return True
|
|
ComfyCache.flush(f'model.{model_name}')
|
|
gc.collect()
|
|
mm.soft_empty_cache()
|
|
return True
|
|
|
|
@classmethod
|
|
def is_installed(cls, model_name: str) -> bool:
|
|
"""
|
|
Check if a vision transformer model is installed in a directory
|
|
"""
|
|
return any(model_name + ".safetensors" in s for s in cls.list_installed())
|
|
|
|
@classmethod
|
|
def list_installed(cls) -> List[str]:
|
|
"""
|
|
Get a list of installed vision transformer models in a directory
|
|
"""
|
|
model_path = os.path.abspath(cls().model_basepath)
|
|
if not os.path.exists(model_path):
|
|
ComfyLogger().log(message=f"Model path {model_path} does not exist, it is being created", type="WARN", always=True)
|
|
os.makedirs(os.path.abspath(model_path))
|
|
return []
|
|
models = list(filter(
|
|
lambda x: x.endswith(".safetensors"), os.listdir(model_path)))
|
|
return models
|
|
|
|
@classmethod
|
|
def download(cls, model_name: str) -> bool:
|
|
"""
|
|
Download a RedRocket JTP Vision Transformer model from a URL
|
|
"""
|
|
os.makedirs(cls().model_basepath, exist_ok=True)
|
|
|
|
config = ComfyExtensionConfig().get()
|
|
hf_endpoint: str = config["huggingface_endpoint"]
|
|
if not hf_endpoint.startswith("https://"):
|
|
hf_endpoint = f"https://{hf_endpoint}"
|
|
if hf_endpoint.endswith("/"):
|
|
hf_endpoint = hf_endpoint.rstrip("/")
|
|
|
|
model_path = os.path.join(cls().model_basepath, f"{model_name}.safetensors")
|
|
|
|
url: str = ComfyExtensionConfig().get(property=f"models.{model_name}.url")
|
|
url = url.replace("{HF_ENDPOINT}", hf_endpoint)
|
|
if not url.endswith("/"):
|
|
url += "/"
|
|
if not url.endswith(".safetensors"):
|
|
url += f"{model_name}.safetensors"
|
|
|
|
ComfyLogger().log(message=f"Downloading model {model_name} from {url}", type="INFO", always=True)
|
|
try:
|
|
response = requests.get(url, stream=True)
|
|
response.raise_for_status()
|
|
|
|
total_size = int(response.headers.get("content-length", 0))
|
|
block_size = 1024
|
|
|
|
pbar = comfy.utils.ProgressBar(total_size) if total_size > 0 else None
|
|
with open(model_path, "wb") as f, tqdm(
|
|
desc=model_name,
|
|
total=total_size,
|
|
unit="iB",
|
|
unit_scale=True,
|
|
unit_divisor=1024,
|
|
) as bar:
|
|
downloaded = 0
|
|
for data in response.iter_content(block_size):
|
|
f.write(data)
|
|
bar.update(len(data))
|
|
downloaded += len(data)
|
|
if pbar is not None:
|
|
pbar.update_absolute(downloaded, total_size)
|
|
|
|
return True
|
|
except Exception as err:
|
|
ComfyLogger().log(
|
|
message=f"Unable to download model. Download files manually or try using a HF mirror/proxy in your config.json: {err}\n{traceback.format_exc()}",
|
|
type="ERROR", always=True,
|
|
)
|
|
return False
|