Files
DemonGatanjieuandClaude Opus 5.5 f65f12152b Fix model kinds misread from headers, checked against real files
Checked against the headers of 315 models in ComfyUI-Manager's list: a Hunyuan-DiT
checkpoint and LBM were read as VAEs, SUPIR as a ControlNet, XLabs' Flux LoRAs and
IP-Adapter as diffusion models, a whole CLIP as a text encoder only; also PhotoMaker, LoRAs
of weight differences or OFT, and checkpoints without a VAE. Files two loaders read from
different folders (SUPIR, a whole CLIP) are not sure. A lone drawing model in checkpoints
loads there (the checkpoint loader reads it as a diffusion model). Newly told: diffusers
UNets, Stable Cascade, SDXL T2I-Adapters, RCAN/SPAN/compact upscalers. 301 of 315 now agree.
"Looks like no drawing model" said what it could not tell: now "Kind not told". Kept header
answers carry the rules version and are read again when it changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 11:15:37 +08:00

378 lines
25 KiB
Python

"""Model import: telling what a model file is from its header (model_kind.py), finding the
dropped file on this computer, and putting it into the models folder without replacing
anything (api/model_import.py). No network; files go to temporary folders."""
import asyncio
import json
import os
import struct
import sys
import tempfile
import time
import unittest
from pathlib import Path
from unittest import mock
from aiohttp import web
from aiohttp.test_utils import TestClient, TestServer
PLUGIN = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(PLUGIN), str(PLUGIN.parents[1])]
import model_identity # noqa: E402
import model_kind # noqa: E402
from api import model_import # noqa: E402
def tensors(*names, shapes=None, meta=None):
header = {name: {"dtype": "F16", "shape": (shapes or {}).get(name, [4, 4]), "data_offsets": [0, 0]} for name in names}
if meta:
header["__metadata__"] = meta
return header
def safetensors_bytes(header):
raw = json.dumps(header).encode("utf-8")
return struct.pack("<Q", len(raw)) + raw + b"\0" * 64
def gguf_bytes(arch, names=()):
def text(value):
raw = value.encode("utf-8")
return struct.pack("<Q", len(raw)) + raw
data = b"GGUF" + struct.pack("<I", 3) + struct.pack("<Q", len(names)) + struct.pack("<Q", 2)
data += text("general.architecture") + struct.pack("<I", 8) + text(arch)
data += text("tokenizer.tokens") + struct.pack("<I", 9) + struct.pack("<I", 8) + struct.pack("<Q", 2) + text("a") + text("b")
for name in names:
data += text(name) + struct.pack("<I", 2) + struct.pack("<QQ", 4, 4) + struct.pack("<I", 1) + struct.pack("<Q", 0)
return data
def kind_of(header):
return model_kind.classify("safetensors", header)
class KindTests(unittest.TestCase):
def test_loras_and_their_base_models(self):
sdxl = kind_of(tensors("lora_unet_input_blocks_4_1_proj_in.lora_down.weight", "lora_te1_text_model_encoder_layers_0_mlp_fc1.alpha"))
self.assertEqual((sdxl["kind"], sdxl["base"], sdxl["sure"]), ("loras", "SDXL", True))
# Diffusers names say nothing: the cross-attention width does (768 = SD 1.5).
sd15 = kind_of(tensors("unet.down_blocks.1.attentions.0.transformer_blocks.0.attn2.to_k.lora_A.weight",
shapes={"unet.down_blocks.1.attentions.0.transformer_blocks.0.attn2.to_k.lora_A.weight": [16, 768]}))
self.assertEqual((sd15["kind"], sd15["base"]), ("loras", "SD 1.5"))
flux = kind_of(tensors("lora_unet_double_blocks_0_img_attn_proj.lora_up.weight"))
self.assertEqual((flux["kind"], flux["base"]), ("loras", "Flux.1 D"))
# Anima (Cosmos-Predict2 with a Qwen3 adapter): Civitai calls it a Checkpoint, it goes in diffusion models.
anima = kind_of(tensors("net.blocks.0.self_attn.q_proj.weight", "net.blocks.0.cross_attn.k_proj.weight",
"net.x_embedder.proj.1.weight", "net.llm_adapter.blocks.0.mlp.0.weight"))
self.assertEqual((anima["kind"], anima["base"], anima["sure"]), ("diffusion_models", "Anima", True))
bare = kind_of(tensors("blocks.0.self_attn.q_proj.weight", "blocks.0.cross_attn.k_proj.weight",
"blocks.0.adaln_modulation_self_attn.1.weight", "llm_adapter.blocks.0.mlp.0.weight"))
self.assertEqual((bare["kind"], bare["base"]), ("diffusion_models", "Anima"))
wan = kind_of(tensors("diffusion_model.blocks.0.self_attn.q.lora_A.weight", "diffusion_model.blocks.0.cross_attn.k.lora_B.weight"))
self.assertEqual(wan["base"], "Wan Video")
# SDXL's families only show in what it was trained on.
illustrious = kind_of(tensors("lora_unet_output_blocks_0_1_proj_in.lora_down.weight",
meta={"ss_base_model_version": "sdxl_base_v1-0", "ss_sd_model_name": "illustriousXL_v01.safetensors"}))
self.assertEqual(illustrious["base"], "Illustrious")
lycoris = kind_of(tensors("lora_unet_down_blocks_0_attentions_0_proj_in.hada_w1_a"))
self.assertEqual(lycoris["kind"], "loras")
def test_whole_models_and_their_parts(self):
checkpoint = kind_of(tensors("model.diffusion_model.input_blocks.0.0.weight", "model.diffusion_model.label_emb.0.0.weight",
"first_stage_model.encoder.down.0.block.0.conv1.weight", "conditioner.embedders.1.model.ln_final.weight"))
self.assertEqual((checkpoint["kind"], checkpoint["base"]), ("checkpoints", "SDXL"))
merged = kind_of(tensors("model.diffusion_model.input_blocks.0.0.weight", "first_stage_model.decoder.up.0.block.0.conv1.weight",
"lora_te_text_model_encoder_layers_0_mlp_fc1.lora_down.weight"))
self.assertEqual(merged["kind"], "checkpoints") # a merge that kept a LoRA's keys
flux = kind_of(tensors("double_blocks.0.img_attn.qkv.weight", "img_in.weight", "single_blocks.0.linear1.weight"))
self.assertEqual((flux["kind"], flux["base"]), ("diffusion_models", "Flux.1 D"))
# Qwen-Image also has img_in: not taken for Flux.
qwen = kind_of(tensors("transformer_blocks.0.img_mod.1.weight", "img_in.weight", "txt_norm.weight"))
self.assertEqual((qwen["kind"], qwen["base"]), ("diffusion_models", "Qwen"))
vae = kind_of(tensors("encoder.down.0.block.0.conv1.weight", "decoder.up.0.block.0.conv1.weight"))
self.assertEqual((vae["kind"], vae["base"], vae["sure"]), ("vae", "", True))
t5 = kind_of(tensors("encoder.block.0.layer.0.SelfAttention.q.weight", "shared.weight"))
self.assertEqual(t5["kind"], "text_encoders")
clip = kind_of(tensors("text_model.encoder.layers.0.mlp.fc1.weight"))
self.assertEqual(clip["kind"], "text_encoders")
vision = kind_of(tensors("vision_model.encoder.layers.0.mlp.fc1.weight"))
self.assertEqual(vision["kind"], "clip_vision")
control = kind_of(tensors("control_model.input_hint_block.0.weight", "control_model.zero_convs.0.0.weight"))
self.assertEqual(control["kind"], "controlnet")
# Model patches: ControlNets by name, but only "Load Model Patch" reads them (models/model_patches).
lllite = kind_of(tensors("lllite_conditioning1.conv1.weight", "lllite_modules.0.down.weight"))
self.assertEqual(lllite, {"kind": "model_patches", "base": "", "sure": True, "form": "lllite"})
blockwise = kind_of(tensors("controlnet_blocks.0.y_rms.weight", "controlnet_blocks.0.img_mod.weight", "img_in.weight"))
self.assertEqual((blockwise["kind"], blockwise["form"]), ("model_patches", ""))
# An IP-Adapter carrying LoRA layers of its own (the Anima IP-Adapter) is no LoRA.
anima_ip = kind_of(tensors("blocks.0.adaln_ip.1.weight", "blocks.0.ip_k_proj.weight",
"lora.base_model.model.blocks.0.cross_attn.k_proj.lora_A.weight"))
self.assertEqual(anima_ip["kind"], "ipadapter")
self.assertEqual(kind_of(tensors("image_proj.proj.weight", "ip_adapter.1.to_k_ip.weight"))["kind"], "ipadapter")
def test_layouts_once_taken_for_another_kind(self):
# Found against ComfyUI-Manager's model list (real headers) and the loaders' own formats.
hunyuan_dit = kind_of(tensors("model.blocks.0.attn1.Wqkv.weight", "text_encoders.mt5xl.transformer.shared.weight",
"vae.encoder.down.0.block.0.conv1.weight"))
self.assertEqual(hunyuan_dit["kind"], "checkpoints") # not its VAE
no_vae = kind_of(tensors("model.diffusion_model.input_blocks.0.0.weight",
"cond_stage_model.transformer.text_model.encoder.layers.0.mlp.fc1.weight"))
self.assertEqual(no_vae["kind"], "checkpoints") # saved without its VAE: still a checkpoint
comfy_lora = kind_of(tensors("diffusion_model.input_blocks.0.0.lora_up.weight",
"text_encoders.clip_l.transformer.text_model.encoder.layers.0.mlp.fc1.lora_up.weight"))
self.assertEqual(comfy_lora["kind"], "loras")
lbm = kind_of(tensors("denoiser.down_blocks.0.resnets.0.conv1.weight", "vae.vae_model.encoder.down_blocks.0.resnets.0.conv1.weight"))
self.assertEqual((lbm["kind"], lbm["sure"]), ("", False)) # a VAE inside another model: not a VAE file
supir = kind_of(tensors("model.control_model.input_hint_block.0.weight", "model.diffusion_model.input_blocks.0.0.weight",
"first_stage_model.denoise_encoder.conv_in.weight"))
self.assertEqual((supir["kind"], supir["sure"]), ("model_patches", False)) # read from model_patches or checkpoints
whole_clip = kind_of(tensors("vision_model.encoder.layers.0.mlp.fc1.weight", "text_model.encoder.layers.0.mlp.fc1.weight"))
self.assertEqual((whole_clip["kind"], whole_clip["sure"]), ("clip_vision", False))
xlabs_lora = kind_of(tensors("double_blocks.0.processor.proj_lora1.down.weight", "double_blocks.0.processor.qkv_lora1.up.weight"))
self.assertEqual(xlabs_lora["kind"], "loras")
xlabs_ip = kind_of(tensors("double_blocks.0.processor.ip_adapter_double_stream_k_proj.weight", "ip_adapter_proj_model.proj.weight"))
self.assertEqual(xlabs_ip["kind"], "ipadapter")
for names in (("diffusion_model.blocks.0.self_attn.q.diff", "diffusion_model.blocks.0.self_attn.q.diff_b"),
("lora_unet_down_blocks_0_attentions_0_proj_in.oft_blocks",),
("lora_unet_x.a1.weight", "lora_unet_x.b1.weight")): # weight differences, OFT, GLoRA
self.assertEqual(kind_of(tensors(*names))["kind"], "loras", names)
photomaker = kind_of(tensors("id_encoder.vision_model.encoder.layers.0.mlp.fc1.weight", "lora_weights.unet.x.lora.down.weight"))
self.assertEqual(photomaker["kind"], "")
hunyuan_video = kind_of(tensors("model.model.double_blocks.0.img_attn.qkv.weight", "model.model.txt_in.input_embedder.weight"))
self.assertEqual(hunyuan_video["kind"], "diffusion_models")
def test_kinds_once_not_told(self):
# Told now, rather than "kind not told" (which the scan page offers to skip).
unet = ("conv_in.weight", "down_blocks.0.resnets.0.conv1.weight", "mid_block.attentions.0.proj_in.weight",
"up_blocks.0.resnets.0.conv1.weight", "time_embedding.linear_1.weight") # diffusers: IC-Light, Kolors
self.assertEqual(kind_of(tensors(*unet))["kind"], "diffusion_models")
motion = kind_of(tensors("down_blocks.0.motion_modules.0.temporal_transformer.proj_in.weight",
"mid_block.motion_modules.0.temporal_transformer.proj_in.weight", "time_embedding.linear_1.weight"))
self.assertEqual(motion["kind"], "") # AnimateDiff, not a diffusion model
vae = kind_of(tensors("encoder.down_blocks.0.resnets.0.conv1.weight", "decoder.mid_block.resnets.0.conv1.weight"))
self.assertEqual(vae["kind"], "vae")
cascade = kind_of(tensors("down_blocks.0.0.channelwise.0.weight", "down_downscalers.1.1.weight", "clf.1.weight"))
self.assertEqual(cascade["kind"], "diffusion_models")
self.assertEqual(kind_of(tensors("adapter.body.0.resnets.0.block1.weight", "adapter.conv_in.weight"))["kind"], "controlnet")
for names in (("head.0.weight", "body.0.body.0.body.0.weight", "tail.0.0.weight"), # RCAN
("conv_1.sk.weight", "block_1.c1_r.sk.weight"), # SPAN
("body.0.weight", "body.1.weight", "body.2.bias")): # compact Real-ESRGAN
found = kind_of(tensors(*names))
self.assertEqual((found["kind"], found["sure"]), ("upscale_models", False), names)
def test_embeddings(self):
self.assertEqual(kind_of(tensors("clip_l", "clip_g")), {"kind": "embeddings", "base": "SDXL", "sure": True, "form": ""})
sd15 = kind_of(tensors("emb_params", shapes={"emb_params": [8, 768]}))
self.assertEqual((sd15["kind"], sd15["base"]), ("embeddings", "SD 1.5"))
def test_unknown_files_are_not_guessed(self):
self.assertEqual(kind_of(tensors("something.weight")), {"kind": "", "base": "", "sure": False, "form": ""})
self.assertEqual(model_kind.classify(None, None), {"kind": "", "base": "", "sure": False})
def test_gguf(self):
flux = model_kind.classify("gguf", model_kind.parse_gguf(gguf_bytes("flux", ["double_blocks.0.img_attn.qkv.weight"])))
self.assertEqual((flux["kind"], flux["base"]), ("diffusion_models", "Flux.1 D"))
self.assertEqual(model_kind.parse_gguf(gguf_bytes("flux", ["a.weight"]))["keys"], ["a.weight"])
self.assertEqual(model_kind.classify("gguf", model_kind.parse_gguf(gguf_bytes("t5encoder")))["kind"], "text_encoders")
# Cut short: what fits is still read.
self.assertEqual(model_kind.parse_gguf(gguf_bytes("wan")[:70])["meta"].get("general.architecture"), "wan")
self.assertIsNone(model_kind.parse_gguf(b"not a gguf"))
def test_the_drawing_models_parameters(self):
with tempfile.TemporaryDirectory() as temp:
checkpoint = os.path.join(temp, "c.safetensors") # its text encoder and VAE are not counted
Path(checkpoint).write_bytes(safetensors_bytes(tensors(
"model.diffusion_model.input_blocks.0.0.weight", "model.diffusion_model.out.2.weight",
"cond_stage_model.transformer.x.weight", "first_stage_model.decoder.x.weight",
shapes={"model.diffusion_model.input_blocks.0.0.weight": [320, 4, 3, 3], "model.diffusion_model.out.2.weight": [4, 320]},
meta={"a": "b"})))
self.assertEqual(model_kind.diffusion_params(checkpoint), 320 * 4 * 9 + 4 * 320)
unet = os.path.join(temp, "u.safetensors")
Path(unet).write_bytes(safetensors_bytes(tensors("double_blocks.0.x.weight", "vae.y.weight", shapes={"double_blocks.0.x.weight": [3072, 3072]})))
self.assertEqual(model_kind.diffusion_params(unet), 3072 * 3072)
gguf = os.path.join(temp, "g.gguf")
Path(gguf).write_bytes(gguf_bytes("flux", ["a.weight", "b.weight"]))
self.assertEqual(model_kind.diffusion_params(gguf), 2 * 4 * 4)
Path(gguf).write_bytes(gguf_bytes("flux", ["a.weight", "b.weight"])[:-10]) # cut short: not a size to go by
self.assertEqual(model_kind.diffusion_params(gguf), 0)
self.assertEqual(model_kind.diffusion_params(os.path.join(temp, "missing.safetensors")), 0)
def test_files_on_disk_and_the_scanner(self):
with tempfile.TemporaryDirectory() as temp:
lora = os.path.join(temp, "x.safetensors")
Path(lora).write_bytes(safetensors_bytes(tensors("lora_unet_double_blocks_0_img_attn_proj.lora_up.weight")))
self.assertEqual(model_kind.inspect_file(lora)["kind"], "loras")
self.assertEqual(model_identity.infer_base_model_from_header(lora), "Flux.1 D")
pickle = os.path.join(temp, "x.ckpt")
Path(pickle).write_bytes(b"\x80\x02whatever")
self.assertEqual(model_kind.inspect_file(pickle), {"kind": "", "base": "", "sure": False, "format": None})
self.assertEqual(model_identity.infer_base_model_from_header(pickle), "Unknown")
broken = os.path.join(temp, "y.safetensors")
Path(broken).write_bytes(struct.pack("<Q", 10 ** 12))
self.assertEqual(model_kind.read_head(broken), (None, None))
class ImportTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
base = os.path.realpath(self.temp.name)
self.loras = os.path.join(base, "models", "loras")
self.downloads = os.path.join(base, "Downloads")
os.makedirs(self.loras)
os.makedirs(os.path.join(self.downloads, "a", "b"))
roots = {"loras": [self.loras], "vae": [os.path.join(base, "models", "vae")]}
self.patches = [
mock.patch.object(model_import.folder_paths, "folder_names_and_paths", {name: (paths, set()) for name, paths in roots.items()}),
mock.patch.object(model_import.folder_paths, "get_folder_paths", side_effect=lambda t: roots.get(t, [])),
mock.patch.object(model_import, "add_entry"),
mock.patch.object(model_import, "forget_file_lists"),
mock.patch.object(model_import, "search_folders", return_value=[("downloads", self.downloads)]),
]
for patch in self.patches:
patch.start()
self.data = safetensors_bytes(tensors("lora_unet_double_blocks_0_img_attn_proj.lora_up.weight"))
def tearDown(self):
for patch in reversed(self.patches):
patch.stop()
self.temp.cleanup()
def dropped(self, *parts):
path = os.path.join(self.downloads, *parts)
Path(path).write_bytes(self.data)
return path, os.stat(path).st_mtime * 1000
def test_the_dropped_file_is_found_by_name_size_and_time(self):
path, mtime = self.dropped("a", "Cool.safetensors")
size = len(self.data)
self.assertEqual(model_import.find_original("cool.safetensors", size, mtime), ("downloads", path))
self.assertIsNone(model_import.find_original("cool.safetensors", size + 1, mtime))
self.assertIsNone(model_import.find_original("cool.safetensors", size, mtime - 60000))
# In subfolders two deep, not deeper.
self.dropped("a", "b", "two.safetensors")
self.assertIsNotNone(model_import.find_original("two.safetensors", size, 0))
os.makedirs(os.path.join(self.downloads, "a", "b", "c"))
self.dropped("a", "b", "c", "three.safetensors")
self.assertIsNone(model_import.find_original("three.safetensors", size, 0))
def test_inspect_found_and_not_found(self):
path, mtime = self.dropped("cool.safetensors")
found = model_import.inspect("cool.safetensors", len(self.data), mtime, b"", local=True)
self.assertEqual((found["kind"], found["base"], found["where"]), ("loras", "Flux.1 D", "downloads"))
self.assertTrue(found["token"])
# Asked from another computer: never looked for, judged from the bytes sent.
remote = model_import.inspect("cool.safetensors", len(self.data), mtime, self.data, local=False)
self.assertNotIn("token", remote)
self.assertEqual(remote["kind"], "loras")
# A type this ComfyUI has no folder for is not offered.
control = safetensors_bytes(tensors("control_model.input_hint_block.0.weight"))
self.assertEqual(model_import.inspect("c.safetensors", len(control), 0, control, local=False)["kind"], "")
def test_place_moves_and_never_replaces(self):
path, mtime = self.dropped("cool.safetensors")
token = model_import.inspect("cool.safetensors", len(self.data), mtime, b"", local=True)["token"]
Path(self.loras, "Flux").mkdir()
Path(self.loras, "Flux", "cool.safetensors").write_bytes(b"older")
placed = model_import.place({"token": token, "type": "loras", "root": 0, "rel": "Flux/cool.safetensors"})
self.assertEqual(placed["rel"], "Flux/cool (2).safetensors")
self.assertFalse(os.path.exists(path))
self.assertEqual(Path(self.loras, "Flux", "cool.safetensors").read_bytes(), b"older")
self.assertEqual(Path(self.loras, "Flux", "cool (2).safetensors").read_bytes(), self.data)
with self.assertRaises(model_import.Refused) as refused:
model_import.place({"token": token, "type": "loras", "root": 0, "rel": "x.safetensors"})
self.assertEqual(refused.exception.code, "gone")
def test_place_keeps_the_original_when_asked(self):
path, mtime = self.dropped("cool.safetensors")
token = model_import.inspect("cool.safetensors", len(self.data), mtime, b"", local=True)["token"]
model_import.place({"token": token, "type": "loras", "root": 0, "rel": "cool.safetensors", "keep": True})
self.assertTrue(os.path.exists(path))
self.assertEqual(Path(self.loras, "cool.safetensors").read_bytes(), self.data)
self.assertFalse(Path(self.loras, "cool.safetensors.part").exists())
def test_a_failed_copy_leaves_nothing_behind(self):
path, _ = self.dropped("cool.safetensors")
dest = os.path.join(self.loras, "cool.safetensors")
with mock.patch.object(model_import.shutil, "copyfile", side_effect=OSError("disk full")):
with self.assertRaises(OSError):
model_import.move_into(path, dest, keep=True)
self.assertTrue(os.path.exists(path))
self.assertFalse(os.path.exists(dest))
def test_a_garbled_copy_is_caught_and_the_original_stays(self):
path, _ = self.dropped("cool.safetensors")
dest = os.path.join(self.loras, "cool.safetensors")
garble = lambda source, target: Path(target).write_bytes(b"x" * len(Path(source).read_bytes())) # right size, wrong bytes
with mock.patch.object(model_import, "_same_drive", return_value=False), \
mock.patch.object(model_import.shutil, "copyfile", side_effect=garble):
with self.assertRaises(model_import.Refused) as refused:
model_import.move_into(path, dest, keep=False)
self.assertEqual(refused.exception.code, "incomplete")
self.assertEqual(Path(path).read_bytes(), self.data)
self.assertFalse(os.path.exists(dest) or os.path.exists(dest + ".part"))
def test_a_file_that_appeared_meanwhile_is_never_replaced(self):
# free_path found the name free; a file took it before the move: the move fails, both stay.
path, _ = self.dropped("cool.safetensors")
dest = os.path.join(self.loras, "cool.safetensors")
Path(dest).write_bytes(b"theirs")
for keep in (False, True):
with self.assertRaises(FileExistsError):
model_import.move_into(path, dest, keep=keep)
self.assertEqual((Path(path).read_bytes(), Path(dest).read_bytes()), (self.data, b"theirs"))
self.assertFalse(os.path.exists(dest + ".part"))
def test_destinations_stay_inside_the_models_folder(self):
for data, code in (({"type": "checkpoints", "rel": "a.safetensors"}, "bad_type"),
({"type": "loras", "root": 3, "rel": "a.safetensors"}, "bad_root"),
({"type": "loras", "rel": "../a.safetensors"}, "bad_path"),
({"type": "loras", "rel": "a.txt"}, "bad_name")):
with self.assertRaises(model_import.Refused) as refused:
model_import.destination(data)
self.assertEqual(refused.exception.code, code)
def test_upload(self):
async def run():
app = web.Application()
model_import.register_routes(app)
async with TestClient(TestServer(app)) as client:
ok = await client.put("/anomalous/import/upload", params={"type": "loras", "root": "0", "rel": "up/new.safetensors",
"size": str(len(self.data))}, data=self.data)
short = await client.put("/anomalous/import/upload", params={"type": "loras", "root": "0", "rel": "up/short.safetensors",
"size": str(len(self.data) + 5)}, data=self.data)
return ok.status, await ok.json(), short.status, await short.json()
status, body, short_status, short_body = asyncio.run(run())
self.assertEqual((status, body["rel"]), (200, "up/new.safetensors"))
self.assertEqual(Path(self.loras, "up", "new.safetensors").read_bytes(), self.data)
self.assertEqual((short_status, short_body["error"]), (400, "incomplete"))
self.assertEqual(sorted(os.listdir(os.path.join(self.loras, "up"))), ["new.safetensors"])
def test_upload_only_from_this_computer(self):
async def run():
app = web.Application()
model_import.register_routes(app)
async with TestClient(TestServer(app)) as client: # through a proxy: not this computer
resp = await client.put("/anomalous/import/upload", headers={"X-Forwarded-For": "192.168.1.20"},
params={"type": "loras", "root": "0", "rel": "far.safetensors", "size": str(len(self.data))},
data=self.data)
return resp.status, await resp.json()
self.assertEqual(asyncio.run(run()), (403, {"error": "not_local"}))
self.assertFalse(Path(self.loras, "far.safetensors").exists())
def test_identify_reports_civitai_and_a_copy_already_there(self):
path, mtime = self.dropped("cool.safetensors")
token = model_import.inspect("cool.safetensors", len(self.data), mtime, b"", local=True)["token"]
record = {"found": True, "base_model": "Illustrious", "model_name": "Cool", "version_name": "v2", "page": "p"}
with mock.patch.object(model_import, "civitai_by_hash", return_value=record), \
mock.patch.object(model_import, "_same_hash", return_value="Flux/old.safetensors"):
found = model_import.identify(token)
self.assertEqual(found["civitai"]["version_name"], "v2")
self.assertEqual(found["duplicate"], "Flux/old.safetensors")
with mock.patch.object(model_import, "civitai_by_hash", side_effect=model_import.SourceUnreachable("down")), \
mock.patch.object(model_import, "_same_hash", return_value=None):
self.assertEqual(model_import.identify(token)["reason"], "network")
if __name__ == "__main__":
unittest.main()