diff --git a/__init__.py b/__init__.py index c74be00..bba137c 100644 --- a/__init__.py +++ b/__init__.py @@ -8,6 +8,7 @@ import comfy.utils import shutil import comfy.samplers + AUTHOR_NAME = u"[MinusZone]" CATEGORY_NAME = f"{AUTHOR_NAME} Utils" @@ -32,7 +33,6 @@ import mz_llava - class MZ_LLamaCPPOptions: @classmethod def INPUT_TYPES(s): @@ -59,6 +59,9 @@ class MZ_LLamaCPPOptions: RETURN_TYPES = ("LLamaCPPOptions",) + RETURN_NAMES = ("llama_cpp_options",) + + FUNCTION = "create" CATEGORY = CATEGORY_NAME def create(self, **kwargs): @@ -125,6 +128,7 @@ class MZ_LLama3CLIPTextEncode: RETURN_TYPES = ("STRING", "CONDITIONING",) + RETURN_NAMES = ("text", "conditioning",) FUNCTION = "encode" CATEGORY = CATEGORY_NAME def encode(self, **kwargs): @@ -179,6 +183,7 @@ class MZ_Phi3CLIPTextEncode: RETURN_TYPES = ("STRING", "CONDITIONING",) + RETURN_NAMES = ("text", "conditioning",) FUNCTION = "encode" CATEGORY = CATEGORY_NAME def encode(self, **kwargs): @@ -221,6 +226,7 @@ class MZ_BaseLLamaCPPCLIPTextEncode: return result RETURN_TYPES = ("STRING", "CONDITIONING",) + RETURN_NAMES = ("text", "conditioning",) FUNCTION = "encode" CATEGORY = CATEGORY_NAME def encode(self, **kwargs): @@ -279,6 +285,7 @@ class MZ_LLavaImageInterrogator: "llama_cpp_options": ("LLamaCPPOptions", ), }} RETURN_TYPES = ("STRING", "CONDITIONING",) + RETURN_NAMES = ("text", "conditioning",) FUNCTION = "interrogate" CATEGORY = CATEGORY_NAME def interrogate(self, **kwargs): @@ -317,6 +324,7 @@ class MZ_BaseLLavaImageInterrogator: "llama_cpp_options": ("LLamaCPPOptions", ), }} RETURN_TYPES = ("STRING", "CONDITIONING",) + RETURN_NAMES = ("text", "conditioning",) FUNCTION = "interrogate" CATEGORY = CATEGORY_NAME def interrogate(self, **kwargs): @@ -352,6 +360,7 @@ class MZ_LLamaCPPInterrogator: }, } RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("text",) FUNCTION = "simple_interrogator" CATEGORY = CATEGORY_NAME def simple_interrogator(self, model_file, prompt, use_system=True, system="You are a helpful assistant.", n_gpu_layers=-1): @@ -367,4 +376,9 @@ class MZ_LLamaCPPInterrogator: # NODE_CLASS_MAPPINGS["MZ_LLamaCPPInterrogator"] = MZ_LLamaCPPInterrogator # NODE_DISPLAY_NAME_MAPPINGS["MZ_LLamaCPPInterrogator"] = f"{AUTHOR_NAME} - LLamaCPP simple interrogator" - \ No newline at end of file + + + + +import mz_gen_translate +mz_gen_translate.gen_translate(NODE_DISPLAY_NAME_MAPPINGS, NODE_CLASS_MAPPINGS) \ No newline at end of file diff --git a/mz_gen_translate.py b/mz_gen_translate.py new file mode 100644 index 0000000..627a7b9 --- /dev/null +++ b/mz_gen_translate.py @@ -0,0 +1,81 @@ + + +import os +import json + + +ZH_Replace_Map = { + "llama_cpp_model": "llama.cpp模型", + "mmproj_model": "mmproj模型", + "resolution": "分辨率", + "sd_format": "SD格式化", + "image": "图像", + "download_source": "下载源", + "prompt_version": "提示词版本", + "style_presets": "风格预设", + "keep_device": "模型常驻显存", + "llama_cpp_options": "llama.cpp可选配置", + "Options": "可选配置", + "LLamaCPP": "llama.cpp", + "CLIPTextEncode": "CLIP文本编码器", + "clip": "CLIP", + "conditioning": "条件", + "ImageInterrogator": "图像反推", +} + + +def gen_translate(NODE_DISPLAY_NAME_MAPPINGS={}, NODE_CLASS_MAPPINGS={}): + translation_dir = os.path.join(os.path.dirname( + os.path.dirname(__file__)), "AIGODLIKE-COMFYUI-TRANSLATION", "zh-CN", "Nodes") + + translation_config = os.path.join( + translation_dir, "ComfyUI_MinusZone.translate.json") + if os.path.exists(translation_dir): + if os.path.exists(translation_config): + translate_config = "{}" + with open(translation_config, "r") as f: + translate_config = f.read() + nodes = json.loads(translate_config) + for key in NODE_DISPLAY_NAME_MAPPINGS: + if key not in nodes: + nodes[key] = {} + + title = NODE_DISPLAY_NAME_MAPPINGS[key] + for k, v in ZH_Replace_Map.items(): + title = title.replace(k, v) + nodes[key]["title"] = title + + if key in NODE_CLASS_MAPPINGS: + node = NODE_CLASS_MAPPINGS[key] + node_INPUT_TYPES = node.INPUT_TYPES() + node_INPUT_TYPES_required = node_INPUT_TYPES.get( + "required", {}) + nodes[key]["widgets"] = {} + for widget_name, _ in node_INPUT_TYPES_required.items(): + widget_name_zh = widget_name + for k, v in ZH_Replace_Map.items(): + widget_name_zh = widget_name_zh.replace(k, v) + nodes[key]["widgets"][widget_name] = widget_name_zh + + node_INPUT_TYPES_optional = node_INPUT_TYPES.get( + "optional", {}) + nodes[key]["inputs"] = {} + for widget_name, _ in node_INPUT_TYPES_optional.items(): + widget_name_zh = widget_name + for k, v in ZH_Replace_Map.items(): + widget_name_zh = widget_name_zh.replace(k, v) + nodes[key]["inputs"][widget_name] = widget_name_zh + + try: + node_RETURN_NAMES = node.RETURN_NAMES + nodes[key]["outputs"] = {} + for widget_name in node_RETURN_NAMES: + widget_name_zh = widget_name + for k, v in ZH_Replace_Map.items(): + widget_name_zh = widget_name_zh.replace(k, v) + nodes[key]["outputs"][widget_name] = widget_name_zh + except: + pass + + with open(translation_config, "w") as f: + f.write(json.dumps(nodes, indent=4, ensure_ascii=False)) diff --git a/mz_llama_cpp.py b/mz_llama_cpp.py index 93b4c42..3214097 100644 --- a/mz_llama_cpp.py +++ b/mz_llama_cpp.py @@ -60,6 +60,7 @@ def check_llama_cpp_requirements(): cuda_version = "cu" + torch.version.cuda.replace(".", "") if cuda_version not in ["cu121", "cu122", "cu123"]: cuda_version = "cu121" + print(f"Warning: The current version of cuda is not supported. (警告: 当前cuda版本不支持) {torch.version.cuda} (默认使用cu121)") else: cuda_version = "cpu" diff --git a/mz_llava.py b/mz_llava.py index a1d8577..eb3a28c 100644 --- a/mz_llava.py +++ b/mz_llava.py @@ -9,6 +9,8 @@ import mz_prompts LLava_models = [ + "llava-1.6-mistral-7b-gguf/llava-1.6-mistral-7b.Q5_K_M.gguf", + "llava-v1.6-vicuna-13b-gguf/llava-v1.6-vicuna-13b.Q5_K_M.gguf", "ggml_llava-v1.5-7b/ggml-model-q4_k.gguf", "ggml_llava-v1.5-7b/ggml-model-q5_k.gguf", "ggml_llava-v1.5-7b/ggml-model-f16.gguf", @@ -18,12 +20,15 @@ LLava_models = [ ] LLava_mmproj_models = [ + "llava-1.6-mistral-7b-gguf/mmproj-model-f16.gguf", + "llava-v1.6-vicuna-13b-gguf/mmproj-model-f16.gguf", "ggml_llava-v1.5-7b/mmproj-model-f16.gguf", "ggml_bakllava-1/mmproj-model-f16.gguf", ] huggingface_models_map = { + "llava-v1.6-vicuna-13b-gguf": "cjpais", "llava-1.6-mistral-7b-gguf": "cjpais", "ggml_llava-v1.5-7b": "mys", "ggml_llava-v1.5-13b": "mys", diff --git a/mz_prompt_utils.py b/mz_prompt_utils.py index 5e5579f..3b35e3d 100644 --- a/mz_prompt_utils.py +++ b/mz_prompt_utils.py @@ -1,16 +1,16 @@ -import os +import os import shutil import subprocess import sys import threading -import time +import time import numpy as np import folder_paths import base64 -from PIL import Image,ImageFilter +from PIL import Image, ImageFilter import io -import torch +import torch import re import hashlib import cv2 @@ -19,25 +19,26 @@ temp_directory = folder_paths.get_temp_directory() from tqdm import tqdm import requests import comfy.utils - - + CACHE_POOL = {} + + class Utils: def Md5(str): return hashlib.md5(str.encode('utf-8')).hexdigest() - + def check_frames_path(frames_path): - + if frames_path == "" or frames_path.startswith(".") or frames_path.startswith("/") or frames_path.endswith("/") or frames_path.endswith("\\"): return "frames_path不能为空" - + frames_path = os.path.join( folder_paths.get_output_directory(), frames_path) - + if frames_path == folder_paths.get_output_directory(): return "frames_path不能为output目录" - + return "" def base64_to_pil_image(base64_str): @@ -53,7 +54,6 @@ class Utils: base64_str = base64.b64decode(base64_str) return Image.open(io.BytesIO(base64_str)) - def pil_image_to_base64(pil_image): buffered = io.BytesIO() pil_image.save(buffered, format="PNG") @@ -61,7 +61,6 @@ class Utils: img_str = str(img_str, encoding="utf-8") return f"data:image/png;base64,{img_str}" - def listdir_png(path): try: files = os.listdir(path) @@ -74,33 +73,30 @@ class Utils: return files except Exception as e: return [] - + def tensor2pil(image): return Image.fromarray(np.clip(255.0 * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)) - # Convert PIL to Tensor + def pil2tensor(image): return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)[0] - def pil2cv(image): return cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) def cv2pil(image): return Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) - + def list_tensor2tensor(data): result_tensor = torch.stack(data) return result_tensor - def loadImage(path): img = Image.open(path) img = img.convert("RGB") return img - - + def vae_encode_crop_pixels(pixels): x = (pixels.shape[1] // 8) * 8 y = (pixels.shape[2] // 8) * 8 @@ -112,61 +108,58 @@ class Utils: def native_vae_encode(vae, image): pixels = Utils.vae_encode_crop_pixels(image) - t = vae.encode(pixels[:,:,:,:3]) - return {"samples":t} + t = vae.encode(pixels[:, :, :, :3]) + return {"samples": t} def native_vae_encode_for_inpaint(vae, pixels, mask): x = (pixels.shape[1] // 8) * 8 y = (pixels.shape[2] // 8) * 8 - mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(pixels.shape[1], pixels.shape[2]), mode="bilinear") + mask = torch.nn.functional.interpolate(mask.reshape( + (-1, 1, mask.shape[-2], mask.shape[-1])), size=(pixels.shape[1], pixels.shape[2]), mode="bilinear") pixels = pixels.clone() if pixels.shape[1] != x or pixels.shape[2] != y: x_offset = (pixels.shape[1] % 8) // 2 y_offset = (pixels.shape[2] % 8) // 2 - pixels = pixels[:,x_offset:x + x_offset, y_offset:y + y_offset,:] - mask = mask[:,:,x_offset:x + x_offset, y_offset:y + y_offset] + pixels = pixels[:, x_offset:x + x_offset, y_offset:y + y_offset, :] + mask = mask[:, :, x_offset:x + x_offset, y_offset:y + y_offset] + + # grow mask by a few pixels to keep things seamless in latent space - #grow mask by a few pixels to keep things seamless in latent space - mask_erosion = mask m = (1.0 - mask.round()).squeeze(1) - for i in range(3): - pixels[:,:,:,i] -= 0.5 - pixels[:,:,:,i] *= m - pixels[:,:,:,i] += 0.5 + for i in range(3): + pixels[:, :, :, i] -= 0.5 + pixels[:, :, :, i] *= m + pixels[:, :, :, i] += 0.5 t = vae.encode(pixels) - return {"samples":t, "noise_mask": (mask_erosion[:,:,:x,:y].round())} + return {"samples": t, "noise_mask": (mask_erosion[:, :, :x, :y].round())} def native_vae_decode(vae, samples): return vae.decode(samples["samples"]) - def native_clip_text_encode(clip,text): + def native_clip_text_encode(clip, text): tokens = clip.tokenize(text) cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True) return [[cond, {"pooled_output": pooled}]] - - - def cache_get(key): + def cache_get(key): return CACHE_POOL.get(key, None) - - def cache_set(key, value): + + def cache_set(key, value): global CACHE_POOL CACHE_POOL[key] = value return True - def get_models_path(): - models_path = os.path.join(folder_paths.models_dir, "minus_zone_models") + models_path = os.path.join( + folder_paths.models_dir, "minus_zone_models") os.makedirs(models_path, exist_ok=True) return models_path - - - def translate_text(text, from_code, to_code): + def translate_text(text, from_code, to_code): try: import argostranslate from argostranslate import translate @@ -177,28 +170,30 @@ class Utils: import argostranslate from argostranslate import translate - - try: - translation = translate.get_translation_from_codes(from_code, to_code) + try: + translation = translate.get_translation_from_codes( + from_code, to_code) if translation is None: raise Exception("Translation not found") - + except Exception as e: - print(e) + print(e) argostranslate.package.update_package_index() available_packages = argostranslate.package.get_available_packages() package_to_install = next( filter( - lambda x: (x.from_code == from_code and x.to_code == to_code), available_packages, - ) + lambda x: (x.from_code == from_code and x.to_code == + to_code), available_packages, + ) ) download_path = package_to_install.download() - print("package_to_install.download():",download_path) + print("package_to_install.download():", download_path) argostranslate.package.install_from_path(download_path) - translation = translate.get_translation_from_codes(from_code, to_code) + translation = translate.get_translation_from_codes( + from_code, to_code) if translation is None: - return text + return text # Translate translatedText = translation.translate( @@ -206,16 +201,12 @@ class Utils: return translatedText - - def zh2en(text): return Utils.translate_text(text, "zh", "en") - def en2zh(text): return Utils.translate_text(text, "en", "zh") - def prompt_zh_to_en(prompt): prompt = prompt.replace(",", ",") prompt = prompt.replace("。", ",") @@ -229,77 +220,78 @@ class Utils: if tags[i][0].isupper(): tags[i] = tags[i].lower().replace(".", "") - return ",".join(tags) def mask_resize(mask, width, height): mask = mask.unsqueeze(0).unsqueeze(0) - mask = torch.nn.functional.interpolate(mask, size=(height, width), mode="bilinear") + mask = torch.nn.functional.interpolate( + mask, size=(height, width), mode="bilinear") mask = mask.squeeze(0).squeeze(0) return mask def mask_threshold(interested_mask): mask_image = Utils.tensor2pil(interested_mask) mask_image_cv2 = Utils.pil2cv(mask_image) - ret,thresh1 = cv2.threshold(mask_image_cv2,127,255,cv2.THRESH_BINARY) + ret, thresh1 = cv2.threshold( + mask_image_cv2, 127, 255, cv2.THRESH_BINARY) thresh1 = Utils.cv2pil(thresh1) thresh1 = np.array(thresh1) - thresh1 = thresh1[:,:,0] + thresh1 = thresh1[:, :, 0] return Utils.pil2tensor(thresh1) def mask_erode(interested_mask, value): value = int(value) mask_image = Utils.tensor2pil(interested_mask) - mask_image_cv2 = Utils.pil2cv(mask_image) - kernel = np.ones((5,5),np.uint8) - erosion = cv2.erode(mask_image_cv2,kernel,iterations = value) + mask_image_cv2 = Utils.pil2cv(mask_image) + kernel = np.ones((5, 5), np.uint8) + erosion = cv2.erode(mask_image_cv2, kernel, iterations=value) erosion = Utils.cv2pil(erosion) erosion = np.array(erosion) - erosion = erosion[:,:,0] - return Utils.pil2tensor(erosion) - + erosion = erosion[:, :, 0] + return Utils.pil2tensor(erosion) + def mask_dilate(interested_mask, value): value = int(value) mask_image = Utils.tensor2pil(interested_mask) - mask_image_cv2 = Utils.pil2cv(mask_image) - kernel = np.ones((5,5),np.uint8) - dilation = cv2.dilate(mask_image_cv2,kernel,iterations = value) + mask_image_cv2 = Utils.pil2cv(mask_image) + kernel = np.ones((5, 5), np.uint8) + dilation = cv2.dilate(mask_image_cv2, kernel, iterations=value) dilation = Utils.cv2pil(dilation) dilation = np.array(dilation) - dilation = dilation[:,:,0] + dilation = dilation[:, :, 0] return Utils.pil2tensor(dilation) - def mask_edge_opt(interested_mask, edge_feathering): - - mask_image = Utils.tensor2pil(interested_mask) - mask_image_cv2 = Utils.pil2cv(mask_image) - - # 高斯模糊 - dilation2 = Utils.cv2pil(mask_image_cv2) - dilation2 = mask_image.filter(ImageFilter.GaussianBlur(edge_feathering)) + mask_image = Utils.tensor2pil(interested_mask) + mask_image_cv2 = Utils.pil2cv(mask_image) + + # 高斯模糊 + dilation2 = Utils.cv2pil(mask_image_cv2) + dilation2 = mask_image.filter( + ImageFilter.GaussianBlur(edge_feathering)) # mask_image dilation2 图片蒙版叠加 dilation2 = Utils.pil2cv(dilation2) # dilation2[mask_image_cv2 < 127] = 0 - dilation2 = Utils.cv2pil(dilation2) + dilation2 = Utils.cv2pil(dilation2) # to RGB dilation2 = np.array(dilation2) - dilation2 = dilation2[:,:,0] + dilation2 = dilation2[:, :, 0] return Utils.pil2tensor(dilation2) - - - def mask_composite(destination, source, x, y, mask = None, multiplier = 8, resize_source = False): + def mask_composite(destination, source, x, y, mask=None, multiplier=8, resize_source=False): source = source.to(destination.device) if resize_source: - source = torch.nn.functional.interpolate(source, size=(destination.shape[2], destination.shape[3]), mode="bilinear") + source = torch.nn.functional.interpolate(source, size=( + destination.shape[2], destination.shape[3]), mode="bilinear") source = comfy.utils.repeat_to_batch_size(source, destination.shape[0]) - x = max(-source.shape[3] * multiplier, min(x, destination.shape[3] * multiplier)) - y = max(-source.shape[2] * multiplier, min(y, destination.shape[2] * multiplier)) + x = max(-source.shape[3] * multiplier, + min(x, destination.shape[3] * multiplier)) + y = max(-source.shape[2] * multiplier, + min(y, destination.shape[2] * multiplier)) left, top = (x // multiplier, y // multiplier) right, bottom = (left + source.shape[3], top + source.shape[2],) @@ -308,44 +300,46 @@ class Utils: mask = torch.ones_like(source) else: mask = mask.to(destination.device, copy=True) - mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(source.shape[2], source.shape[3]), mode="bilinear") + mask = torch.nn.functional.interpolate(mask.reshape( + (-1, 1, mask.shape[-2], mask.shape[-1])), size=(source.shape[2], source.shape[3]), mode="bilinear") mask = comfy.utils.repeat_to_batch_size(mask, source.shape[0]) # calculate the bounds of the source that will be overlapping the destination # this prevents the source trying to overwrite latent pixels that are out of bounds # of the destination - visible_width, visible_height = (destination.shape[3] - left + min(0, x), destination.shape[2] - top + min(0, y),) + visible_width, visible_height = ( + destination.shape[3] - left + min(0, x), destination.shape[2] - top + min(0, y),) mask = mask[:, :, :visible_height, :visible_width] inverse_mask = torch.ones_like(mask) - mask source_portion = mask * source[:, :, :visible_height, :visible_width] - destination_portion = inverse_mask * destination[:, :, top:bottom, left:right] + destination_portion = inverse_mask * \ + destination[:, :, top:bottom, left:right] - destination[:, :, top:bottom, left:right] = source_portion + destination_portion + destination[:, :, top:bottom, + left:right] = source_portion + destination_portion return destination - def latent_upscale_by(samples, scale_by): s = samples.copy() width = round(samples["samples"].shape[3] * scale_by) height = round(samples["samples"].shape[2] * scale_by) - s["samples"] = comfy.utils.common_upscale(samples["samples"], width, height, "nearest-exact", "disabled") + s["samples"] = comfy.utils.common_upscale( + samples["samples"], width, height, "nearest-exact", "disabled") return s - def resize_by(image, percent): # 判断类型是否为PIL if not isinstance(image, Image.Image): image = Image.fromarray(image) - + width, height = image.size new_width = int(width * percent) new_height = int(height * percent) return image.resize((new_width, new_height), Image.LANCZOS) - - def resize_max(im, dst_w, dst_h): + def resize_max(im, dst_w, dst_h): src_w, src_h = im.size if src_h < src_w: @@ -359,22 +353,20 @@ class Utils: newWidth = newWidth // 8 * 8 return im.resize((newWidth, newHeight), Image.Resampling.LANCZOS) - - def get_device(): return comfy.model_management.get_torch_device() - - - def download_file(url, filepath, threads=8): - + + def download_file(url, filepath, threads=8, retries=6): + get_size_tmp = requests.get(url, stream=True) total_size = int(get_size_tmp.headers.get("content-length", 0)) print(f"Downloading {url} to {filepath} with size {total_size} bytes") - + base_filename = os.path.basename(filepath) - cache_dir = os.path.join(os.path.dirname(filepath), f"{base_filename}.t_{threads}_cache") + cache_dir = os.path.join(os.path.dirname( + filepath), f"{base_filename}.t_{threads}_cache") os.makedirs(cache_dir, exist_ok=True) def get_total_existing_size(): @@ -382,21 +374,18 @@ class Utils: existing_size = 0 for f in fs: if f.startswith("block_"): - existing_size += os.path.getsize(os.path.join(cache_dir, f)) + existing_size += os.path.getsize( + os.path.join(cache_dir, f)) return existing_size - total_existing_size = get_total_existing_size() - + if total_size != 0 and total_existing_size != total_size: - with tqdm(total=total_size, initial=total_existing_size, unit="B", unit_scale=True) as progress_bar: all_threads = [] - - - for i in range(threads): + for i in range(threads): cache_filepath = os.path.join(cache_dir, f"block_{i}") start = total_size // threads * i @@ -412,7 +401,6 @@ class Utils: else: existing_size = 0 - headers = {"Range": f"bytes={start + existing_size}-{end}"} if end == total_size: headers = {"Range": f"bytes={start + existing_size}-"} @@ -422,7 +410,7 @@ class Utils: # Streaming, so we can iterate over the response. response = requests.get(url, stream=True, headers=headers) - + def download_file_thread(response, cache_filepath): block_size = 1024 if end - (start + existing_size) < block_size: @@ -434,15 +422,15 @@ class Utils: len(data) ) - t = threading.Thread(target=download_file_thread, args=(response, cache_filepath)) - + t = threading.Thread( + target=download_file_thread, args=(response, cache_filepath)) + all_threads.append(t) t.start() for t in all_threads: t.join() - if total_size != 0 and get_total_existing_size() > total_size: # 文件下载失败 @@ -450,12 +438,19 @@ class Utils: raise RuntimeError("Download failed, file is incomplete") if total_size != 0 and total_size != get_total_existing_size(): - # 文件损坏 - raise RuntimeError(f"Download failed: {total_size} != {get_total_existing_size()}") - + if retries > 0: + retries -= 1 + print( + f"Download failed: {total_size} != {get_total_existing_size()}, retrying... {retries} retries left") + return Utils.download_file(url, filepath, threads, retries) + + # 文件损坏 + raise RuntimeError( + f"Download failed: {total_size} != {get_total_existing_size()}") + if os.path.exists(filepath): - shutil.move(filepath, filepath + ".old." + time.strftime("%Y%m%d%H%M%S")) - + shutil.move(filepath, filepath + ".old." + + time.strftime("%Y%m%d%H%M%S")) # merge the files with open(filepath, "wb") as f: @@ -463,16 +458,16 @@ class Utils: cache_filepath = os.path.join(cache_dir, f"block_{i}") with open(cache_filepath, "rb") as cf: f.write(cf.read()) - + shutil.rmtree(cache_dir) return filepath - def hf_download_model(url, only_get_path=False): if not url.startswith("https://"): raise ValueError("URL must start with https://") if url.startswith("https://huggingface.co/") or url.startswith("https://hf-mirror.com/"): - base_model_path = os.path.abspath(os.path.join(Utils.get_models_path(), "transformers_models")) + base_model_path = os.path.abspath(os.path.join( + Utils.get_models_path(), "transformers_models")) # https://huggingface.co/FaradayDotDev/llama-3-8b-Instruct-GGUF/resolve/main/llama-3-8b-Instruct.Q2_K.gguf?download=true texts = url.split("?")[0].split("/") file_name = texts[-1] @@ -483,10 +478,12 @@ class Utils: if os.path.exists(save_path) is False: if only_get_path: return None - os.makedirs(os.path.join(base_model_path, zone_path), exist_ok=True) + os.makedirs(os.path.join( + base_model_path, zone_path), exist_ok=True) Utils.download_file(url, save_path) - Utils.print_log(f"File {save_path} => {os.path.getsize(save_path)} ") + Utils.print_log( + f"File {save_path} => {os.path.getsize(save_path)} ") # 获取大小 if os.path.getsize(save_path) == 0: @@ -495,17 +492,19 @@ class Utils: os.remove(save_path) raise ValueError(f"Download failed: {url}") return save_path - else: + else: texts = url.split("?")[0].split("/") host = texts[2].replace(".", "_") - base_model_path = os.path.abspath(os.path.join(Utils.get_models_path(), f"{host}_models")) + base_model_path = os.path.abspath(os.path.join( + Utils.get_models_path(), f"{host}_models")) file_name = texts[-1] file_name_no_ext = os.path.splitext(file_name)[0] file_ext = os.path.splitext(file_name)[1] md5_hash = Utils.Md5(url) - save_path = os.path.join(base_model_path, f"{file_name_no_ext}.{md5_hash}{file_ext}") + save_path = os.path.join( + base_model_path, f"{file_name_no_ext}.{md5_hash}{file_ext}") if os.path.exists(save_path) is False: if only_get_path: @@ -515,18 +514,16 @@ class Utils: return save_path - def print_log(*args): if os.environ.get("MZ_DEV", None) is not None: print(*args) - - + def modelscope_download_model(model_type, model_name, only_get_path=False): if model_type not in modelscope_models_map: if only_get_path: return None raise ValueError(f"模型类型 {model_type} 不支持") - + if model_name not in modelscope_models_map[model_type]: if only_get_path: return None @@ -538,15 +535,17 @@ class Utils: model_info = modelscope_models_map[model_type][model_name] url = model_info["url"] output = model_info["output"] - save_path = os.path.abspath(os.path.join(Utils.get_models_path(), output)) + save_path = os.path.abspath( + os.path.join(Utils.get_models_path(), output)) if not os.path.exists(save_path): if only_get_path: return None save_path = Utils.download_file(url, save_path) return save_path + modelscope_models_map = { - "llama3":{ + "llama3": { "Meta-Llama-3-8B-Instruct.Q4_K_M.gguf": { "url": "https://modelscope.cn/api/v1/models/LLM-Research/Meta-Llama-3-8B-Instruct-GGUF/repo?Revision=master&FilePath=Meta-Llama-3-8B-Instruct-Q4_K_M.gguf", "output": "modelscope_models/Meta-Llama-3-8B-Instruct-GGUF/Meta-Llama-3-8B-Instruct-Q4_K_M.gguf", @@ -558,35 +557,53 @@ modelscope_models_map = { "SHA256": "1ea28c7c5f2a1ada991c40940a59bd42668939feeafb125d0764c38ae815b2a3", }, }, - "llava":{ - "ggml_bakllava-1/ggml-model-q5_k.gguf":{ + "llava": { + "ggml_bakllava-1/ggml-model-q5_k.gguf": { "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=ggml_bakllava-1%2Fggml-model-q5_k.gguf", "output": "modelscope_models/ggml_bakllava-1/ggml-model-q5_k.gguf", "SHA256": "c93de1376be9b6977cc94d252a3d165d6059e07b528de0fa762534d9599b27d6", }, - "ggml_llava-v1.5-7b/ggml-model-q4_k.gguf":{ + "ggml_llava-v1.5-7b/ggml-model-q4_k.gguf": { "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=ggml_llava-v1.5-7b%2Fggml-model-q4_k.gguf", "output": "modelscope_models/ggml_llava-v1.5-7b/ggml-model-q4_k.gguf", "SHA256": "7ac9c2f7b8d76cc7f3118cdf0953ebab7a7a9b12bad5dbe237219d2ab61765ea", }, - "ggml_bakllava-1/mmproj-model-f16.gguf":{ + "ggml_bakllava-1/mmproj-model-f16.gguf": { "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=ggml_bakllava-1%2Fmmproj-model-f16.gguf", "output": "modelscope_models/ggml_bakllava-1/mmproj-model-f16.gguf", "SHA256": "2e467eba710002839e0966d5e329942bb836eabd4e787bc713b07eff1d8ea13b" }, - "ggml_llava-v1.5-7b/mmproj-model-f16.gguf":{ + "ggml_llava-v1.5-7b/mmproj-model-f16.gguf": { "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=ggml_llava-v1.5-7b%2Fmmproj-model-f16.gguf", "output": "modelscope_models/ggml_llava-v1.5-7b/mmproj-model-f16.gguf", "SHA256": "b7c8ff0f58fca47d28ba92c4443adf8653f3349282cb8d9e6911f22d9b3814fe" }, - + "llava-v1.6-vicuna-13b-gguf/llava-v1.6-vicuna-13b.Q5_K_M.gguf": { + "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=llava-v1.6-vicuna-13b-gguf%2Fllava-v1.6-vicuna-13b.Q5_K_M.gguf", + "output": "modelscope_models/llava-v1.6-vicuna-13b-gguf/llava-v1.6-vicuna-13b.Q5_K_M.gguf", + "SHA256": "fac8ecd642ac1c2dd8ef8fb6d6daac349293051b468f5fdc21921b94dfb2916a", + }, + "llava-v1.6-vicuna-13b-gguf/mmproj-model-f16.gguf": { + "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=llava-v1.6-vicuna-13b-gguf%2Fmmproj-model-f16.gguf", + "output": "modelscope_models/llava-v1.6-vicuna-13b-gguf/mmproj-model-f16.gguf", + "SHA256": "9860fbc162d6df40d12f32211aed11ab4826e16e473e6fff595496b9943872e2" + }, + "llava-1.6-mistral-7b-gguf/llava-1.6-mistral-7b.Q5_K_M.gguf": { + "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=llava-1.6-mistral-7b-gguf%2Fllava-v1.6-mistral-7b.Q5_K_M.gguf", + "output": "modelscope_models/llava-1.6-mistral-7b-gguf/llava-v1.6-mistral-7b.Q5_K_M.gguf", + "SHA256": "b1d37fc65ecb80aa8f1ce185bf4d7605bc3c5cc5bcc77a160c3a1b0377631112", + }, + "llava-1.6-mistral-7b-gguf/mmproj-model-f16.gguf": { + "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=llava-1.6-mistral-7b-gguf%2Fmmproj-model-f16.gguf", + "output": "modelscope_models/llava-1.6-mistral-7b-gguf/mmproj-model-f16.gguf", + "SHA256": "00205ee8a0d7a381900cd031e43105f86aa0d8c07bf329851e85c71a26632d16", + }, }, - "phi3":{ - "Phi-3-mini-4k-instruct-q4.gguf":{ - "url":"https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=Phi-3-mini-4k-instruct-gguf%2FPhi-3-mini-4k-instruct-q4.gguf", - "output":"modelscope_models/Phi-3-mini-4k-instruct-gguf/Phi-3-mini-4k-instruct-q4.gguf", - "SHA256":"1cd9a9df07350196623f93bf4829cf228959e07ad32f787b8fdd7f5956f5b9de" + "phi3": { + "Phi-3-mini-4k-instruct-q4.gguf": { + "url": "https://www.modelscope.cn/api/v1/models/wailovet/MinusZoneAIModels/repo?Revision=master&FilePath=Phi-3-mini-4k-instruct-gguf%2FPhi-3-mini-4k-instruct-q4.gguf", + "output": "modelscope_models/Phi-3-mini-4k-instruct-gguf/Phi-3-mini-4k-instruct-q4.gguf", + "SHA256": "1cd9a9df07350196623f93bf4829cf228959e07ad32f787b8fdd7f5956f5b9de" } }, } -