feat(metadata): resolve AutoV3 at download time without waiting for backfill

- Read AutoV3 directly from the downloaded file's own file_info hashes
  (no SHA256 cross-matching against version_info.files, so the value is
  captured even when the API omits SHA256)
- Extract normalize_autov3() validation helper shared with the
  sha256-matching autov3_from_civitai_files path
- Fall back to the embedded safetensors header hash at download
  completion; mark '' (checked-unavailable) so the startup backfill
  query (autov3 IS NULL) never revisits the row
- Clear archive-level AutoV3 for zip-extracted models so per-file
  header resolution applies to every extracted model
This commit is contained in:
Will Miao
2026-08-08 15:20:43 +08:00
parent 97b9b1f62b
commit 6fcdeb799d
6 changed files with 197 additions and 18 deletions

View File

@@ -75,6 +75,7 @@ async def test_execute_download_retries_urls(monkeypatch, tmp_path):
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
return os.path.basename(self.file_path)
@@ -164,6 +165,7 @@ async def test_execute_download_uses_aria2_backend_for_model_files(monkeypatch,
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
return os.path.basename(self.file_path)
@@ -270,6 +272,7 @@ async def test_execute_download_allows_anonymous_civitai_with_aria2(
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
return os.path.basename(self.file_path)
@@ -347,6 +350,7 @@ async def test_execute_download_adjusts_checkpoint_sub_type(monkeypatch, tmp_pat
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
self.preview_nsfw_level = 0
self.sub_type = "checkpoint"
@@ -446,6 +450,7 @@ async def test_execute_download_extracts_zip_single_model(monkeypatch, tmp_path)
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
return os.path.basename(self.file_path)
@@ -458,6 +463,9 @@ async def test_execute_download_extracts_zip_single_model(monkeypatch, tmp_path)
return {"file_path": self.file_path}
metadata = DummyMetadata(zip_path)
# Simulate a zip entry with an API-reported AutoV3: it must be cleared
# for extracted models, which resolve their own header hash instead.
metadata.autov3 = "zipzipzip123"
version_info = {"images": []}
download_urls = ["https://example.invalid/model.zip"]
@@ -503,6 +511,9 @@ async def test_execute_download_extracts_zip_single_model(monkeypatch, tmp_path)
assert saved_call.args[0] == str(extracted)
# SHA256 comes from metadata (API value), not recalculated
assert saved_call.args[1].sha256 == "sha256"
# The zip-level AutoV3 was cleared; the extracted file is not safetensors,
# so the entry is marked checked-unavailable rather than inheriting it.
assert saved_call.args[1].autov3 == ""
assert dummy_scanner.add_model_to_cache.await_count == 1
@@ -520,6 +531,7 @@ async def test_execute_download_extracts_zip_multiple_models(monkeypatch, tmp_pa
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
return os.path.basename(self.file_path)
@@ -602,6 +614,7 @@ async def test_execute_download_extracts_zip_pt_embedding(monkeypatch, tmp_path)
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
return os.path.basename(self.file_path)
@@ -1155,6 +1168,7 @@ async def test_execute_download_waits_for_paused_pre_transfer_gate(monkeypatch,
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
return os.path.basename(self.file_path)
@@ -1264,6 +1278,7 @@ async def test_execute_download_reuses_existing_aria2_partial_path(monkeypatch,
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
return "renamed.safetensors"
@@ -1341,6 +1356,7 @@ async def test_execute_download_rejects_conflicting_aria2_partial_path(tmp_path)
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
raise AssertionError("should not rename")
@@ -1392,6 +1408,7 @@ async def test_execute_download_reassigns_same_aria2_partial_to_new_download_id(
self.sha256 = "sha256"
self.file_name = path.stem
self.preview_url = None
self.autov3 = None
def generate_unique_filename(self, *_args, **_kwargs):
raise AssertionError("should not rename")