fix(import): request withMeta=true from CivitAI API, fix checkpoint type guard and CivArchive version lookup

- Add &withMeta=true to image info URL so API returns full generation
  metadata (resources with hash/type) instead of null meta
- Fix checkpoint assignment guard: check modelId instead of id so non-
  checkpoint types (upscaler) are not wrongly set as recipe checkpoint
- Skip modelVersionIds loop when resources/civitaiResources already
  provided LoRAs, preventing hash-resolved duplicates
- Fix int/str type comparison in CivArchive get_model_version so
  version ID matching works correctly
This commit is contained in:
Will Miao
2026-06-27 22:22:48 +08:00
parent 283730cf38
commit 703a6a4ea0
4 changed files with 21 additions and 10 deletions

View File

@@ -514,11 +514,21 @@ class CivitaiApiMetadataParser(RecipeMetadataParser):
result["loras"].append(lora_entry)
# Process modelVersionIds from Civitai image API
# These are model version IDs returned at root level when meta doesn't contain resources
if "modelVersionIds" in metadata and isinstance(
metadata["modelVersionIds"], list
# Process modelVersionIds from Civitai image API.
# These are version IDs returned at root level of the API response.
# When resources or civitaiResources are already present in metadata
# (which they are when ?withMeta=true is passed), those sections have
# complete hash/type information — modelVersionIds is a fallback for
# when meta is null and only the flat ID list is available. Skipping
# it here avoids duplicates: the same file hash often resolves to
# different version IDs via hash lookup (resources) vs the original
# version ID in modelVersionIds, and both paths would create entries.
if (
"modelVersionIds" in metadata
and isinstance(metadata["modelVersionIds"], list)
and not result.get("loras")
):
for version_id in metadata["modelVersionIds"]:
version_id_str = str(version_id)
@@ -594,11 +604,12 @@ class CivitaiApiMetadataParser(RecipeMetadataParser):
checkpoint_entry, civitai_info
)
)
if cp_populated.get("id"):
if cp_populated.get("modelId"):
result["model"] = cp_populated
continue # Not a LoRA, don't add to loras
lora_entry = populated_entry
except Exception as e:
logger.error(
f"Error fetching Civitai info for model version {version_id}: {e}"

View File

@@ -417,7 +417,7 @@ class CivArchiveClient:
if version_id is not None:
raw_id = version_data.get("id")
if raw_id != version_id:
if raw_id is not None and str(raw_id) != str(version_id):
logger.warning(
"Requested version %s doesn't match default version %s for model %s",
version_id,

View File

@@ -56,7 +56,7 @@ class CivitaiClient:
self._MAX_CACHE_ENTRIES = 500
def _build_image_info_url(self, image_id: str) -> str:
return f"{self.base_url}/images?imageId={image_id}&nsfw=X"
return f"{self.base_url}/images?imageId={image_id}&nsfw=X&withMeta=true"
async def _make_request(
self,

View File

@@ -568,7 +568,7 @@ async def test_get_image_info_prefers_red_host_for_red_source(monkeypatch, downl
assert result == {"id": 124950237, "name": "target"}
assert requested_urls == [
"https://civitai.red/api/v1/images?imageId=124950237&nsfw=X"
"https://civitai.red/api/v1/images?imageId=124950237&nsfw=X&withMeta=true"
]
@@ -589,7 +589,7 @@ async def test_get_image_info_uses_red_host_even_for_red_source(monkeypatch, dow
assert result == {"id": 124950237, "name": "target"}
assert requested_urls == [
"https://civitai.red/api/v1/images?imageId=124950237&nsfw=X",
"https://civitai.red/api/v1/images?imageId=124950237&nsfw=X&withMeta=true",
]
@@ -610,7 +610,7 @@ async def test_get_image_info_does_not_fall_back_after_request_failure(monkeypat
assert result is None
assert requested_urls == [
"https://civitai.red/api/v1/images?imageId=124950237&nsfw=X",
"https://civitai.red/api/v1/images?imageId=124950237&nsfw=X&withMeta=true",
]