mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-20 18:51:26 -03:00
feat(backend): make model existence checks other-aware
Implements the backend slice (B1-B7) of lm-civitai-extension/docs/other-models-support.md, which lets the companion browser extension detect, badge and download the opt-in Other Models types (VAE / upscaler / text encoder / CLIP vision / ControlNet). ModelLibraryHandler: - _normalize_model_type() learns the CivitAI other aliases (vae, upscaler, textencoder, clip, clipvision, controlnet, other) and maps them to "other". - _get_scanner_for_type() resolves "other" through the other scanner, but only while enable_other_models is on, so model-versions-status and model-version-download-status keep their legacy 400 when the feature is off. - check_model_exists() / check_models_exist() consult the other scanner last (lora -> checkpoint -> embedding -> other) and report modelType "other". With the feature disabled both endpoints stay byte-identical to before and the other scanner is never touched. DownloadManager: - The four other-type default-path failures now carry a machine-readable "reason" (contract C4): other_models_disabled, other_sub_type_disabled, other_no_default_root, other_sub_type_undecidable. The user-facing "error" strings are unchanged; the key is additive and reaches the client because both download endpoints pass the result dict through verbatim. Tests cover the opt-in on/off branches for both existence endpoints, mixed lora + other ids in the batch endpoint, the CivitAI alias acceptance and the 400 regression for unknown types, and the exact reason/error pairs for all four download failure modes.
This commit is contained in:
@@ -2113,6 +2113,8 @@ class ModelLibraryHandler:
|
||||
return "checkpoint"
|
||||
if normalized in {"embedding", "textualinversion"}:
|
||||
return "embedding"
|
||||
if normalized in VALID_OTHER_CIVITAI_TYPES:
|
||||
return "other"
|
||||
return None
|
||||
|
||||
async def _get_scanner_for_type(self, model_type: str | None):
|
||||
@@ -2123,6 +2125,13 @@ class ModelLibraryHandler:
|
||||
return normalized_type, await self._service_registry.get_checkpoint_scanner()
|
||||
if normalized_type == "embedding":
|
||||
return normalized_type, await self._service_registry.get_embedding_scanner()
|
||||
if normalized_type == "other":
|
||||
# Opt-in feature: the other scanner only resolves while the master
|
||||
# switch is on, so callers keep returning the legacy "required"
|
||||
# error (400) when it is off.
|
||||
if not get_settings_manager().is_other_models_enabled():
|
||||
return None, None
|
||||
return normalized_type, await self._service_registry.get_other_scanner()
|
||||
return None, None
|
||||
|
||||
async def _get_download_history_service(self):
|
||||
@@ -2214,6 +2223,11 @@ class ModelLibraryHandler:
|
||||
lora_scanner = await self._service_registry.get_lora_scanner()
|
||||
checkpoint_scanner = await self._service_registry.get_checkpoint_scanner()
|
||||
embedding_scanner = await self._service_registry.get_embedding_scanner()
|
||||
# Opt-in: probe the other scanner only while Other Models is enabled,
|
||||
# so the disabled behaviour stays byte-identical to the legacy one.
|
||||
other_scanner = None
|
||||
if get_settings_manager().is_other_models_enabled():
|
||||
other_scanner = await self._service_registry.get_other_scanner()
|
||||
|
||||
if model_version_id_str:
|
||||
try:
|
||||
@@ -2252,6 +2266,13 @@ class ModelLibraryHandler:
|
||||
exists = True
|
||||
model_type = "embedding"
|
||||
matched_scanner = embedding_scanner
|
||||
elif (
|
||||
other_scanner
|
||||
and await other_scanner.check_model_version_exists(model_version_id)
|
||||
):
|
||||
exists = True
|
||||
model_type = "other"
|
||||
matched_scanner = other_scanner
|
||||
|
||||
if exists:
|
||||
return web.json_response(
|
||||
@@ -2269,7 +2290,7 @@ class ModelLibraryHandler:
|
||||
history_service = await self._get_download_history_service()
|
||||
has_been_downloaded = False
|
||||
history_type = None
|
||||
for candidate_type in ("lora", "checkpoint", "embedding"):
|
||||
for candidate_type in ("lora", "checkpoint", "embedding", "other"):
|
||||
if await history_service.has_been_downloaded(
|
||||
candidate_type,
|
||||
model_version_id,
|
||||
@@ -2291,6 +2312,7 @@ class ModelLibraryHandler:
|
||||
lora_versions = await lora_scanner.get_model_versions_by_id(model_id)
|
||||
checkpoint_versions = []
|
||||
embedding_versions = []
|
||||
other_versions = []
|
||||
if not lora_versions and checkpoint_scanner:
|
||||
checkpoint_versions = await checkpoint_scanner.get_model_versions_by_id(
|
||||
model_id
|
||||
@@ -2299,6 +2321,13 @@ class ModelLibraryHandler:
|
||||
embedding_versions = await embedding_scanner.get_model_versions_by_id(
|
||||
model_id
|
||||
)
|
||||
if (
|
||||
not lora_versions
|
||||
and not checkpoint_versions
|
||||
and not embedding_versions
|
||||
and other_scanner
|
||||
):
|
||||
other_versions = await other_scanner.get_model_versions_by_id(model_id)
|
||||
|
||||
model_type = None
|
||||
versions = []
|
||||
@@ -2330,9 +2359,18 @@ class ModelLibraryHandler:
|
||||
"downloadedVersionIds": [],
|
||||
}
|
||||
)
|
||||
if other_versions:
|
||||
return web.json_response(
|
||||
{
|
||||
"success": True,
|
||||
"modelType": "other",
|
||||
"versions": self._with_downloaded_flag(other_versions),
|
||||
"downloadedVersionIds": [],
|
||||
}
|
||||
)
|
||||
|
||||
history_service = await self._get_download_history_service()
|
||||
for candidate_type in ("lora", "checkpoint", "embedding"):
|
||||
for candidate_type in ("lora", "checkpoint", "embedding", "other"):
|
||||
candidate_downloaded_version_ids = (
|
||||
await history_service.get_downloaded_version_ids(
|
||||
candidate_type,
|
||||
@@ -2387,6 +2425,11 @@ class ModelLibraryHandler:
|
||||
lora_scanner = await self._service_registry.get_lora_scanner()
|
||||
checkpoint_scanner = await self._service_registry.get_checkpoint_scanner()
|
||||
embedding_scanner = await self._service_registry.get_embedding_scanner()
|
||||
# Opt-in: keep the other probe last so model cards for lora /
|
||||
# checkpoint / embedding ids are unaffected by the extra scanner.
|
||||
other_scanner = None
|
||||
if get_settings_manager().is_other_models_enabled():
|
||||
other_scanner = await self._service_registry.get_other_scanner()
|
||||
|
||||
results: list[dict[str, Any]] = []
|
||||
for model_id in model_ids:
|
||||
@@ -2422,6 +2465,17 @@ class ModelLibraryHandler:
|
||||
})
|
||||
continue
|
||||
|
||||
if other_scanner:
|
||||
other_versions = await other_scanner.get_model_versions_by_id(model_id)
|
||||
if other_versions:
|
||||
results.append({
|
||||
"modelId": model_id,
|
||||
"modelType": "other",
|
||||
"versions": self._with_downloaded_flag(other_versions),
|
||||
"downloadedVersionIds": [],
|
||||
})
|
||||
continue
|
||||
|
||||
results.append({
|
||||
"modelId": model_id,
|
||||
"modelType": None,
|
||||
|
||||
@@ -1534,6 +1534,9 @@ class DownloadManager:
|
||||
"Settings > Library before downloading VAE, upscaler, "
|
||||
"text encoder or CLIP files."
|
||||
),
|
||||
# Machine-readable failure code consumed by the companion
|
||||
# browser extension (docs/other-models-support.md C4).
|
||||
"reason": "other_models_disabled",
|
||||
}
|
||||
model_type = "other"
|
||||
else:
|
||||
@@ -1793,6 +1796,7 @@ class DownloadManager:
|
||||
f"disabled in settings. Please pick a destination "
|
||||
f"folder explicitly instead of using default paths."
|
||||
),
|
||||
"reason": "other_sub_type_disabled",
|
||||
}
|
||||
default_path = (
|
||||
default_other_roots.get(other_sub_type)
|
||||
@@ -1805,17 +1809,20 @@ class DownloadManager:
|
||||
f"No default root configured for other-model "
|
||||
f"sub-type '{other_sub_type}'"
|
||||
)
|
||||
reason = "other_no_default_root"
|
||||
else:
|
||||
detail = (
|
||||
"Could not determine the other-model sub-type "
|
||||
"from the model metadata"
|
||||
)
|
||||
reason = "other_sub_type_undecidable"
|
||||
return {
|
||||
"success": False,
|
||||
"error": (
|
||||
f"{detail}. Please pick a destination folder "
|
||||
f"explicitly instead of using default paths."
|
||||
),
|
||||
"reason": reason,
|
||||
}
|
||||
save_dir = default_path
|
||||
|
||||
|
||||
Reference in New Issue
Block a user