mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-10-03 00:35:32 -03:00
Merge pull request #1132 from willmiao/fix/diffusion-routing-default
fix(downloads): route unknown checkpoint baseModels to diffusion models by default
This commit is contained in:
@@ -103,6 +103,15 @@ Locales: `en`, `zh-CN`, `zh-TW`, `ja`, `ko`, `fr`, `de`, `es`, `ru`, `he` (RTL).
|
||||
> failure-table conventions; reuse each locale's existing renderings of those sibling keys).
|
||||
> All 9 locales are translated in the same pass (terminology in §2, "Sidecar storage feature").
|
||||
|
||||
> **Status (2026-10, unknown base model routing):** the download-routing inversion added
|
||||
> 4 keys (`settings.unknownBaseModelRouting.label`, `.help`, `.options.diffusionModel`,
|
||||
> `.options.checkpoint`) — the option labels reuse each locale's existing
|
||||
> `checkpoints.modelTypes.diffusion_model` / `.checkpoint` renderings. The same pass also
|
||||
> translated the leftover `doctor.issues.sidecar_mirror_orphans.title` ("Centralized
|
||||
> Sidecars", §2 "Sidecar storage feature" terminology). All 9 locales are translated,
|
||||
> so the "no remaining placeholders" claim holds again. Terminology in §2, "Download
|
||||
> routing feature".
|
||||
|
||||
---
|
||||
|
||||
## 1. Hard rules (do not violate)
|
||||
@@ -543,6 +552,25 @@ short and imperative and do not append a keyboard hint in any locale.
|
||||
|
||||
The grip itself is an icon and is never translated.
|
||||
|
||||
### Download routing feature (unknown base model routing)
|
||||
|
||||
The `settings.unknownBaseModelRouting.*` keys (Settings → Library → Folder Settings) decide
|
||||
which library a checkpoint download lands in when CivitAI reports a baseModel that is in
|
||||
neither the known-checkpoint nor the known-diffusion-model list. The option labels reuse
|
||||
each locale's `checkpoints.modelTypes.diffusion_model` / `.checkpoint` renderings
|
||||
(model-type names, R3 — ja/ko keep the Latin loanword), pluralized only where the locale
|
||||
pluralizes (de Diffusionsmodelle, es Modelos de difusión, fr Modèles de diffusion,
|
||||
ru Диффузионные модели, he מודלי דיפוזיה; CJK stays singular, "Checkpoint(s)" follows
|
||||
`header.navigation.checkpoints`).
|
||||
|
||||
| Term | Rendering |
|
||||
|---|---|
|
||||
| routing (noun, of a download into a library) | zh-CN 路由 · zh-TW 路由 · ja 振り分け · ko 라우팅 · fr routage · de Routing · es enrutamiento · ru маршрутизация · he ניתוב |
|
||||
| unknown base model | zh-CN 未知基础模型 · zh-TW 未知基礎模型 · ja 不明なベースモデル · ko 알 수 없는 베이스 모델 · fr modèle de base inconnu · de unbekanntes Basismodell · es modelo base desconocido · ru неизвестная базовая модель · he מודל בסיס לא מוכר |
|
||||
|
||||
The baseModel family names in the help text (`SD 1.x/2.x/3.x, SDXL, Pony, Illustrious,
|
||||
NoobAI`) are CivitAI baseModel values and stay verbatim in every locale.
|
||||
|
||||
---
|
||||
|
||||
## 3. Cross-cutting confusion hot-spots (must-fix list)
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "Leer lassen, um aria2c aus dem PATH zu verwenden"
|
||||
},
|
||||
"aria2HelpLink": "Erfahren Sie, wie Sie das aria2-Download-Backend einrichten",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "Routing unbekannter Basismodelle",
|
||||
"help": "Legt fest, wohin Checkpoint-Downloads gehen, wenn CivitAI ein Basismodell meldet, das weder ein bekanntes Checkpoint (SD 1.x/2.x/3.x, SDXL, Pony, Illustrious, NoobAI) noch ein bekanntes Diffusionsmodell ist. Neue Diffusionsarchitekturen erscheinen häufig, daher ist das Routing zu Diffusionsmodellen meist korrekt.",
|
||||
"options": {
|
||||
"diffusionModel": "Diffusionsmodelle",
|
||||
"checkpoint": "Checkpoints"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "CivitAI-Host-Einstellung verfügbar",
|
||||
"content": "CivitAI verwendet jetzt civitai.com für SFW-Inhalte und civitai.red für uneingeschränkte Inhalte. In den Einstellungen können Sie ändern, welche Seite standardmäßig geöffnet wird.",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "Konflikte durch doppelte Dateinamen"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "Zentralisierte Sidecar-Dateien"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "UI-Version"
|
||||
|
||||
@@ -353,6 +353,14 @@
|
||||
"placeholder": "Leave empty to use aria2c from PATH"
|
||||
},
|
||||
"aria2HelpLink": "Learn how to set up the aria2 download backend",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "Unknown base model routing",
|
||||
"help": "Decides where checkpoint downloads go when CivitAI reports a base model that is neither a known checkpoint (SD 1.x/2.x/3.x, SDXL, Pony, Illustrious, NoobAI) nor a known diffusion model. New diffusion architectures appear frequently, so routing them to diffusion models is usually correct.",
|
||||
"options": {
|
||||
"diffusionModel": "Diffusion models",
|
||||
"checkpoint": "Checkpoints"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "CivitAI host preference available",
|
||||
"content": "CivitAI now uses civitai.com for SFW content and civitai.red for unrestricted content. You can change which site opens by default in Settings.",
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "Déjalo vacío para usar aria2c desde el PATH"
|
||||
},
|
||||
"aria2HelpLink": "Aprende a configurar el backend de descarga aria2",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "Enrutamiento de modelos base desconocidos",
|
||||
"help": "Decide a dónde van las descargas de Checkpoint cuando CivitAI informa de un modelo base que no es ni un Checkpoint conocido (SD 1.x/2.x/3.x, SDXL, Pony, Illustrious, NoobAI) ni un modelo de difusión conocido. Las nuevas arquitecturas de difusión aparecen con frecuencia, por lo que enrutarlas a modelos de difusión suele ser correcto.",
|
||||
"options": {
|
||||
"diffusionModel": "Modelos de difusión",
|
||||
"checkpoint": "Checkpoints"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "Preferencia de host de CivitAI disponible",
|
||||
"content": "CivitAI ahora usa civitai.com para contenido SFW y civitai.red para contenido sin restricciones. Puedes cambiar en Ajustes qué sitio se abre por defecto.",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "Conflictos de nombres de archivo duplicados"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "Archivos sidecar centralizados"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "Versión de la interfaz"
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "Laisser vide pour utiliser aria2c depuis le PATH"
|
||||
},
|
||||
"aria2HelpLink": "Apprenez à configurer le backend de téléchargement aria2",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "Routage des modèles de base inconnus",
|
||||
"help": "Détermine où vont les téléchargements de Checkpoint lorsque CivitAI signale un modèle de base qui n’est ni un Checkpoint connu (SD 1.x/2.x/3.x, SDXL, Pony, Illustrious, NoobAI) ni un modèle de diffusion connu. De nouvelles architectures de diffusion apparaissent fréquemment, donc les router vers les modèles de diffusion est généralement correct.",
|
||||
"options": {
|
||||
"diffusionModel": "Modèles de diffusion",
|
||||
"checkpoint": "Checkpoints"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "Préférence d’hôte CivitAI disponible",
|
||||
"content": "CivitAI utilise désormais civitai.com pour le contenu SFW et civitai.red pour le contenu sans restriction. Vous pouvez modifier dans les paramètres le site ouvert par défaut.",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "Conflits de noms de fichiers en double"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "Fichiers sidecar centralisés"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "Version de l'interface"
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "השאר ריק כדי להשתמש ב-aria2c מתוך ה-PATH"
|
||||
},
|
||||
"aria2HelpLink": "למד כיצד להגדיר את מנוע ההורדה aria2",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "ניתוב מודל בסיס לא מוכר",
|
||||
"help": "קובע לאן מועברות הורדות של Checkpoint כאשר CivitAI מדווח על מודל בסיס שהוא לא Checkpoint מוכר (SD 1.x/2.x/3.x, SDXL, Pony, Illustrious, NoobAI) ולא מודל דיפוזיה מוכר. ארכיטקטורות דיפוזיה חדשות מופיעות לעיתים קרובות, ולכן ניתוב שלהן למודלי דיפוזיה הוא בדרך כלל הנכון.",
|
||||
"options": {
|
||||
"diffusionModel": "מודלי דיפוזיה",
|
||||
"checkpoint": "Checkpoints"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "העדפת מארח CivitAI זמינה",
|
||||
"content": "CivitAI משתמש כעת ב-civitai.com עבור תוכן SFW וב-civitai.red עבור תוכן ללא הגבלות. ניתן לשנות בהגדרות איזה אתר ייפתח כברירת מחדל.",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "התנגשויות שמות קבצים כפולים"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "קבצי לוואי מרוכזים"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "גרסת הממשק"
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "空欄のままにすると PATH 上の aria2c を使用します"
|
||||
},
|
||||
"aria2HelpLink": "aria2 ダウンロードバックエンドの設定方法",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "不明なベースモデルの振り分け",
|
||||
"help": "CivitAIが報告するベースモデルが既知のCheckpoint(SD 1.x/2.x/3.x、SDXL、Pony、Illustrious、NoobAI)でも既知のDiffusion Modelでもない場合に、Checkpointのダウンロードの保存先を決定します。新しい拡散アーキテクチャは頻繁に登場するため、通常はDiffusion Modelへの振り分けが正しい選択です。",
|
||||
"options": {
|
||||
"diffusionModel": "Diffusion Model",
|
||||
"checkpoint": "Checkpoint"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "CivitAI ホスト設定を利用できます",
|
||||
"content": "CivitAI は現在、SFW コンテンツには civitai.com、制限なしコンテンツには civitai.red を使用しています。設定で既定で開くサイトを変更できます。",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "ファイル名重複競合"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "集中保存のサイドカーファイル"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "UI バージョン"
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "비워 두면 PATH의 aria2c를 사용합니다"
|
||||
},
|
||||
"aria2HelpLink": "aria2 다운로드 백엔드 설정 방법 알아보기",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "알 수 없는 베이스 모델 라우팅",
|
||||
"help": "CivitAI가 보고한 베이스 모델이 알려진 Checkpoint(SD 1.x/2.x/3.x, SDXL, Pony, Illustrious, NoobAI)도 알려진 Diffusion Model도 아닐 때 Checkpoint 다운로드의 저장 위치를 결정합니다. 새로운 확산 아키텍처는 자주 등장하므로 일반적으로 Diffusion Model로 라우팅하는 것이 올바릅니다.",
|
||||
"options": {
|
||||
"diffusionModel": "Diffusion Model",
|
||||
"checkpoint": "Checkpoint"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "CivitAI 호스트 기본 설정 사용 가능",
|
||||
"content": "이제 CivitAI는 SFW 콘텐츠에 civitai.com을, 무제한 콘텐츠에 civitai.red를 사용합니다. 설정에서 기본으로 열 사이트를 변경할 수 있습니다.",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "파일명 중복 충돌"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "중앙 집중식 사이드카 파일"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "UI 버전"
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "Оставьте пустым, чтобы использовать aria2c из PATH"
|
||||
},
|
||||
"aria2HelpLink": "Узнайте, как настроить сервер загрузки aria2",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "Маршрутизация неизвестных базовых моделей",
|
||||
"help": "Определяет, куда отправляются загрузки Checkpoint, когда CivitAI сообщает базовую модель, которая не является ни известным Checkpoint (SD 1.x/2.x/3.x, SDXL, Pony, Illustrious, NoobAI), ни известной диффузионной моделью. Новые диффузионные архитектуры появляются часто, поэтому маршрутизация их в диффузионные модели обычно правильная.",
|
||||
"options": {
|
||||
"diffusionModel": "Диффузионные модели",
|
||||
"checkpoint": "Checkpoints"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "Доступна настройка хоста CivitAI",
|
||||
"content": "Теперь CivitAI использует civitai.com для контента SFW и civitai.red для контента без ограничений. В настройках можно изменить, какой сайт открывать по умолчанию.",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "Конфликты дублирующихся имён файлов"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "Централизованные sidecar-файлы"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "Версия интерфейса"
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "留空则使用 PATH 中的 aria2c"
|
||||
},
|
||||
"aria2HelpLink": "了解如何配置 aria2 下载后端",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "未知基础模型路由",
|
||||
"help": "当 CivitAI 报告的基础模型既不是已知的 Checkpoint(SD 1.x/2.x/3.x、SDXL、Pony、Illustrious、NoobAI),也不是已知的扩散模型时,决定 Checkpoint 下载的保存位置。新的扩散架构频繁出现,因此将其路由到扩散模型通常是正确的。",
|
||||
"options": {
|
||||
"diffusionModel": "扩散模型",
|
||||
"checkpoint": "Checkpoint"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "已提供 CivitAI 站点偏好设置",
|
||||
"content": "CivitAI 现在使用 civitai.com 提供 SFW 内容,使用 civitai.red 提供无限制内容。你可以在设置中更改默认打开的站点。",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "文件名重复冲突"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "集中存储的附属文件"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "UI 版本"
|
||||
|
||||
+9
-1
@@ -353,6 +353,14 @@
|
||||
"placeholder": "留空則使用 PATH 中的 aria2c"
|
||||
},
|
||||
"aria2HelpLink": "了解如何設定 aria2 下載後端",
|
||||
"unknownBaseModelRouting": {
|
||||
"label": "未知基礎模型路由",
|
||||
"help": "當 CivitAI 回報的基礎模型既不是已知的 Checkpoint(SD 1.x/2.x/3.x、SDXL、Pony、Illustrious、NoobAI),也不是已知的擴散模型時,決定 Checkpoint 下載的儲存位置。新的擴散架構頻繁出現,因此將其路由到擴散模型通常是正確的。",
|
||||
"options": {
|
||||
"diffusionModel": "擴散模型",
|
||||
"checkpoint": "Checkpoint"
|
||||
}
|
||||
},
|
||||
"civitaiHostBanner": {
|
||||
"title": "已提供 CivitAI 站點偏好設定",
|
||||
"content": "CivitAI 現在使用 civitai.com 提供 SFW 內容,使用 civitai.red 提供無限制內容。您可以在設定中變更預設開啟的站點。",
|
||||
@@ -2770,7 +2778,7 @@
|
||||
"title": "檔案名稱重複衝突"
|
||||
},
|
||||
"sidecar_mirror_orphans": {
|
||||
"title": "[TODO: Translate] Centralized Sidecars"
|
||||
"title": "集中儲存的附屬檔案"
|
||||
},
|
||||
"ui_version": {
|
||||
"title": "UI 版本"
|
||||
|
||||
@@ -55,6 +55,11 @@ class DownloadRoutingHandler:
|
||||
status=400,
|
||||
)
|
||||
|
||||
# CivitAI ModelType.UNet downloads go through the checkpoint branch,
|
||||
# same as in the download manager.
|
||||
if model_type.lower() == "unet":
|
||||
model_type = "checkpoint"
|
||||
|
||||
if model_type.lower() in VALID_OTHER_CIVITAI_TYPES:
|
||||
from ...services.settings_manager import get_settings_manager
|
||||
|
||||
@@ -96,10 +101,15 @@ class DownloadRoutingHandler:
|
||||
}
|
||||
)
|
||||
|
||||
from ...services.settings_manager import get_settings_manager
|
||||
|
||||
is_diffusion = is_diffusion_model_download(
|
||||
model_type,
|
||||
file_types=(str(t) for t in file_types),
|
||||
base_model=base_model,
|
||||
unknown_base_model_default=get_settings_manager().get(
|
||||
"unknown_base_model_routing", "diffusion_model"
|
||||
),
|
||||
)
|
||||
return web.json_response(
|
||||
{
|
||||
|
||||
@@ -1523,7 +1523,11 @@ class DownloadManager:
|
||||
return {"success": False, "error": "Failed to fetch model metadata"}
|
||||
|
||||
model_type_from_info = version_info.get("model", {}).get("type", "").lower()
|
||||
if model_type_from_info == "checkpoint":
|
||||
# CivitAI ModelType has no model-level diffusion variant: DiT
|
||||
# models are uploaded as "Checkpoint" or "UNet". Both go through
|
||||
# the checkpoint branch so the standard diffusion routing chain
|
||||
# (file type -> baseModel lists -> unknown default) applies.
|
||||
if model_type_from_info in ("checkpoint", "unet"):
|
||||
model_type = "checkpoint"
|
||||
elif model_type_from_info in VALID_LORA_TYPES:
|
||||
model_type = "lora"
|
||||
@@ -1670,6 +1674,9 @@ class DownloadManager:
|
||||
model_type,
|
||||
file_types=(f.get("type", "") for f in version_info.get("files", [])),
|
||||
base_model=base_model_value,
|
||||
unknown_base_model_default=get_settings_manager().get(
|
||||
"unknown_base_model_routing", "diffusion_model"
|
||||
),
|
||||
)
|
||||
|
||||
# Existence check after the metadata fetch (#1058):
|
||||
|
||||
@@ -13,6 +13,7 @@ import logging
|
||||
from typing import Iterable, Optional
|
||||
|
||||
from ..utils.constants import (
|
||||
CHECKPOINT_BASE_MODELS,
|
||||
CIVITAI_FILE_TYPE_TO_OTHER_SUB_TYPE,
|
||||
CIVITAI_TYPE_TO_OTHER_SUB_TYPE,
|
||||
DIFFUSION_MODEL_BASE_MODELS,
|
||||
@@ -24,17 +25,27 @@ logger = logging.getLogger(__name__)
|
||||
# model (loaded via UNETLoader in ComfyUI) rather than a full checkpoint.
|
||||
DIFFUSION_FILE_TYPES = frozenset({"UNet", "Diffusion Model"})
|
||||
|
||||
# Allowed values for the "unknown_base_model_routing" setting / the
|
||||
# unknown_base_model_default parameter below.
|
||||
ROUTING_DIFFUSION_MODEL = "diffusion_model"
|
||||
ROUTING_CHECKPOINT = "checkpoint"
|
||||
|
||||
|
||||
def is_diffusion_model_download(
|
||||
model_type: str,
|
||||
file_types: Iterable[str] = (),
|
||||
base_model: str = "",
|
||||
unknown_base_model_default: str = ROUTING_DIFFUSION_MODEL,
|
||||
) -> bool:
|
||||
"""Return True when a download should be routed to the unet roots.
|
||||
|
||||
Only applies to downloads initiated from the checkpoint library.
|
||||
Priority: (1) any file has type "UNet" or "Diffusion Model" (the more
|
||||
direct signal from CivitAI), (2) baseModel is a known diffusion model.
|
||||
direct signal from CivitAI), (2) baseModel is a known diffusion model,
|
||||
(3) baseModel is a known full checkpoint -> not diffusion, (4) unknown
|
||||
or empty baseModel -> the ``unknown_base_model_default`` setting, which
|
||||
defaults to diffusion because the set of true checkpoint families is
|
||||
closed while new DiT base models appear all the time.
|
||||
"""
|
||||
if model_type != "checkpoint":
|
||||
return False
|
||||
@@ -54,7 +65,16 @@ def is_diffusion_model_download(
|
||||
)
|
||||
return True
|
||||
|
||||
return False
|
||||
if base_model in CHECKPOINT_BASE_MODELS:
|
||||
return False
|
||||
|
||||
is_diffusion = unknown_base_model_default != ROUTING_CHECKPOINT
|
||||
logger.info(
|
||||
"baseModel '%s' is unknown, routing to %s folder (unknown_base_model_routing)",
|
||||
base_model,
|
||||
"unet" if is_diffusion else "checkpoint",
|
||||
)
|
||||
return is_diffusion
|
||||
|
||||
|
||||
def resolve_other_download_sub_type(
|
||||
|
||||
@@ -130,6 +130,9 @@ DEFAULT_SETTINGS: Dict[str, Any] = {
|
||||
"metadata_refresh_skip_paths": [],
|
||||
"skip_previously_downloaded_model_versions": False,
|
||||
"download_skip_base_models": [],
|
||||
# Routing target for checkpoint downloads whose baseModel is neither a
|
||||
# known diffusion model nor a known full checkpoint (CHECKPOINT_BASE_MODELS).
|
||||
"unknown_base_model_routing": "diffusion_model",
|
||||
"backup_auto_enabled": True,
|
||||
"backup_retention_count": 5,
|
||||
"use_new_license_icons": True,
|
||||
@@ -1628,6 +1631,16 @@ class SettingsManager:
|
||||
return normalized
|
||||
return "alongside"
|
||||
|
||||
@staticmethod
|
||||
def _normalize_unknown_base_model_routing(value: Any) -> str:
|
||||
"""Return a valid unknown-base-model routing target, falling back to ``diffusion_model``."""
|
||||
|
||||
if isinstance(value, str):
|
||||
normalized = value.strip().lower()
|
||||
if normalized in ("diffusion_model", "checkpoint"):
|
||||
return normalized
|
||||
return "diffusion_model"
|
||||
|
||||
def _refresh_sidecar_storage_config(self) -> None:
|
||||
"""Rebuild dependent config state after sidecar storage settings change."""
|
||||
|
||||
@@ -1840,6 +1853,8 @@ class SettingsManager:
|
||||
self._migrate_recipes_directory(current_recipes_dir, target_recipes_dir)
|
||||
elif key == "sidecar_storage_mode":
|
||||
value = self._normalize_sidecar_storage_mode(value)
|
||||
elif key == "unknown_base_model_routing":
|
||||
value = self._normalize_unknown_base_model_routing(value)
|
||||
elif key == "sidecar_storage_path":
|
||||
value = self._normalize_recipes_path_value(value)
|
||||
self.settings[key] = value
|
||||
|
||||
@@ -352,12 +352,15 @@ DIFFUSION_MODEL_BASE_MODELS = frozenset(
|
||||
"PixArt E",
|
||||
# Video diffusion models
|
||||
"CogVideoX",
|
||||
"Flux 3 Video",
|
||||
"Hunyuan Video",
|
||||
"LTXV",
|
||||
"LTXV2",
|
||||
"LTXV 2.3",
|
||||
"LTXV 2.5",
|
||||
"Mochi",
|
||||
"SVD",
|
||||
"SVD XT",
|
||||
"Wan Video",
|
||||
"Wan Video 1.3B t2v",
|
||||
"Wan Video 14B t2v",
|
||||
@@ -368,11 +371,19 @@ DIFFUSION_MODEL_BASE_MODELS = frozenset(
|
||||
"Wan Video 2.2 T2V-A14B",
|
||||
"Wan Video 2.5 T2V",
|
||||
"Wan Video 2.5 I2V",
|
||||
"Wan Video 2.7",
|
||||
"Wan Video 3.0",
|
||||
# Other diffusion models
|
||||
"Boogu",
|
||||
"Ernie",
|
||||
"Ernie Turbo",
|
||||
"HiDream-O1",
|
||||
"Ming Image Design 0.1",
|
||||
"Ming Image Design Layer 0.1",
|
||||
"MiniMax H3",
|
||||
"Nucleus",
|
||||
"Qwen",
|
||||
"Wan Image 2.7",
|
||||
"ZImageBase",
|
||||
"ZImageTurbo",
|
||||
# Krea 2 — loaded via UNETLoader in ComfyUI
|
||||
@@ -380,6 +391,53 @@ DIFFUSION_MODEL_BASE_MODELS = frozenset(
|
||||
]
|
||||
)
|
||||
|
||||
# baseModel values from CivitAI that are true full checkpoints (loaded via
|
||||
# CheckpointLoaderSimple in ComfyUI). New DiT families appear on CivitAI all
|
||||
# the time, so download routing inverts the fallback: anything NOT in this
|
||||
# closed set (and not a known diffusion model) is treated as a diffusion
|
||||
# model by default (see py/services/download_routing.py). Cross-checked
|
||||
# against CivitAI's official baseModelRecords (packages/civitai-shared
|
||||
# src/basemodel.constants.ts), not only the download skip list.
|
||||
# "Pony V7" is deliberately excluded: verified via the live API (model
|
||||
# 1901521) to be AuraFlow-architecture shipping .gguf variants (UNETLoader),
|
||||
# so it follows the unknown-base-model default (diffusion).
|
||||
CHECKPOINT_BASE_MODELS = frozenset(
|
||||
[
|
||||
# Stable Diffusion 1.x
|
||||
"SD 1.4",
|
||||
"SD 1.5",
|
||||
"SD 1.5 LCM",
|
||||
"SD 1.5 Hyper",
|
||||
# Stable Diffusion 2.x
|
||||
"SD 2.0",
|
||||
"SD 2.0 768",
|
||||
"SD 2.1",
|
||||
"SD 2.1 768",
|
||||
"SD 2.1 Unclip",
|
||||
# Stable Diffusion 3.x
|
||||
"SD 3",
|
||||
"SD 3.5",
|
||||
"SD 3.5 Medium",
|
||||
"SD 3.5 Large",
|
||||
"SD 3.5 Large Turbo",
|
||||
# SDXL and its full-checkpoint derivatives
|
||||
"SDXL 0.9",
|
||||
"SDXL 1.0",
|
||||
"SDXL 1.0 LCM",
|
||||
"SDXL Lightning",
|
||||
"SDXL Hyper",
|
||||
"SDXL Turbo",
|
||||
"SDXL Distilled",
|
||||
"Pony",
|
||||
"Illustrious",
|
||||
"NoobAI",
|
||||
# Other full-checkpoint families
|
||||
"Playground v2",
|
||||
# Stable Cascade is unCLIP-style but loads via CheckpointLoader
|
||||
"Stable Cascade",
|
||||
]
|
||||
)
|
||||
|
||||
# Supported baseModel values for download exclusion settings.
|
||||
# Keep this aligned with static/js/utils/constants.js, excluding the generic "Other" value.
|
||||
SUPPORTED_DOWNLOAD_SKIP_BASE_MODELS = frozenset(
|
||||
|
||||
@@ -1053,6 +1053,11 @@ export class SettingsManager {
|
||||
downloadBackendSelect.value = state.global.settings.download_backend || 'python';
|
||||
}
|
||||
|
||||
const unknownBaseModelRoutingSelect = document.getElementById('unknownBaseModelRouting');
|
||||
if (unknownBaseModelRoutingSelect) {
|
||||
unknownBaseModelRoutingSelect.value = state.global.settings.unknown_base_model_routing || 'diffusion_model';
|
||||
}
|
||||
|
||||
const aria2cPathInput = document.getElementById('aria2cPath');
|
||||
if (aria2cPathInput) {
|
||||
aria2cPathInput.value = state.global.settings.aria2c_path || '';
|
||||
|
||||
@@ -60,6 +60,7 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({
|
||||
metadata_refresh_skip_paths: [],
|
||||
skip_previously_downloaded_model_versions: false,
|
||||
download_skip_base_models: [],
|
||||
unknown_base_model_routing: 'diffusion_model',
|
||||
backup_auto_enabled: true,
|
||||
backup_retention_count: 5,
|
||||
sidecar_storage_mode: 'alongside',
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
|
||||
{{ sm.setting_select('defaultUnetRoot', 'default_unet_root', 'settings.folderSettings.defaultUnetRoot', [], 'settings.folderSettings.defaultUnetRootHelp') }}
|
||||
|
||||
{{ sm.setting_select('unknownBaseModelRouting', 'unknown_base_model_routing', 'settings.unknownBaseModelRouting.label', [
|
||||
('diffusion_model', 'settings.unknownBaseModelRouting.options.diffusionModel'),
|
||||
('checkpoint', 'settings.unknownBaseModelRouting.options.checkpoint'),
|
||||
], 'settings.unknownBaseModelRouting.help') }}
|
||||
|
||||
{{ sm.setting_select('defaultEmbeddingRoot', 'default_embedding_root', 'settings.folderSettings.defaultEmbeddingRoot', [], 'settings.folderSettings.defaultEmbeddingRootHelp') }}
|
||||
|
||||
{{ sm.setting_toggle('enableOtherModels', 'enable_other_models', 'settings.folderSettings.enableOtherModels', 'settings.folderSettings.enableOtherModelsHelp') }}
|
||||
|
||||
@@ -73,6 +73,57 @@ async def test_regular_checkpoint_stays_on_checkpoint_root():
|
||||
assert payload["root_kind"] == "checkpoint"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unknown_base_model_routes_to_unet_by_default():
|
||||
handler = DownloadRoutingHandler()
|
||||
response = await handler.get_download_routing(
|
||||
FakeRequest(
|
||||
{
|
||||
"model_type": "checkpoint",
|
||||
"base_model": "Brand New Arch",
|
||||
"file_types": ["Model"],
|
||||
}
|
||||
)
|
||||
)
|
||||
payload = json.loads(response.text)
|
||||
assert payload["is_diffusion_model"] is True
|
||||
assert payload["root_kind"] == "unet"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unknown_base_model_honors_checkpoint_routing_setting():
|
||||
get_settings_manager().settings["unknown_base_model_routing"] = "checkpoint"
|
||||
|
||||
handler = DownloadRoutingHandler()
|
||||
response = await handler.get_download_routing(
|
||||
FakeRequest(
|
||||
{
|
||||
"model_type": "checkpoint",
|
||||
"base_model": "Brand New Arch",
|
||||
"file_types": ["Model"],
|
||||
}
|
||||
)
|
||||
)
|
||||
payload = json.loads(response.text)
|
||||
assert payload["is_diffusion_model"] is False
|
||||
assert payload["root_kind"] == "checkpoint"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unet_model_type_uses_checkpoint_branch():
|
||||
"""CivitAI ModelType.UNet has no branch of its own: it goes through the
|
||||
checkpoint branch, where its UNet-typed files route to the unet roots."""
|
||||
handler = DownloadRoutingHandler()
|
||||
response = await handler.get_download_routing(
|
||||
FakeRequest(
|
||||
{"model_type": "unet", "base_model": "MiniMax H3", "file_types": ["UNet"]}
|
||||
)
|
||||
)
|
||||
payload = json.loads(response.text)
|
||||
assert payload["is_diffusion_model"] is True
|
||||
assert payload["root_kind"] == "unet"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lora_is_never_diffusion():
|
||||
handler = DownloadRoutingHandler()
|
||||
|
||||
@@ -2200,6 +2200,54 @@ async def test_download_uses_raw_file_name_from_mini_endpoint(
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unet_model_type_download_lands_in_unet_roots(
|
||||
monkeypatch, scanners, metadata_provider, tmp_path
|
||||
):
|
||||
"""CivitAI ModelType.UNet goes through the checkpoint branch: its
|
||||
UNet-typed files route to the unet roots like any diffusion model."""
|
||||
manager = DownloadManager()
|
||||
unet_root = tmp_path / "unet"
|
||||
get_settings_manager().settings["default_unet_root"] = str(unet_root)
|
||||
metadata_provider.payload = {
|
||||
"id": 42,
|
||||
"model": {"type": "UNet", "tags": ["fantasy"]},
|
||||
"baseModel": "MiniMax H3",
|
||||
"creator": {"username": "Author"},
|
||||
"files": [
|
||||
{
|
||||
"id": 1001,
|
||||
"type": "UNet",
|
||||
"primary": True,
|
||||
"name": "minimax_h3.safetensors",
|
||||
"downloadUrl": "https://example.invalid/file.safetensors",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
captured = {}
|
||||
|
||||
async def fake_execute_download(self, **kwargs):
|
||||
captured["file_path"] = kwargs["metadata"].file_path
|
||||
return {"success": True}
|
||||
|
||||
monkeypatch.setattr(
|
||||
DownloadManager, "_execute_download", fake_execute_download, raising=False
|
||||
)
|
||||
|
||||
result = await manager.download_from_civitai(
|
||||
model_version_id=42,
|
||||
save_dir=str(tmp_path),
|
||||
use_default_paths=True,
|
||||
progress_callback=None,
|
||||
source=None,
|
||||
)
|
||||
|
||||
assert result["success"] is True, result
|
||||
assert captured["file_path"].startswith(str(unet_root))
|
||||
assert captured["file_path"].endswith("minimax_h3.safetensors")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_download_falls_back_to_rest_name_when_mini_fails(
|
||||
monkeypatch, scanners, metadata_provider, tmp_path
|
||||
@@ -2209,7 +2257,7 @@ async def test_download_falls_back_to_rest_name_when_mini_fails(
|
||||
metadata_provider.payload = {
|
||||
"id": 42,
|
||||
"model": {"type": "Checkpoint", "tags": ["fantasy"]},
|
||||
"baseModel": "BaseModel",
|
||||
"baseModel": "SDXL 1.0",
|
||||
"creator": {"username": "Author"},
|
||||
"files": [
|
||||
{
|
||||
|
||||
@@ -20,9 +20,68 @@ def test_base_model_fallback_routes_to_unet():
|
||||
)
|
||||
|
||||
|
||||
def test_regular_checkpoint_stays_on_checkpoint_roots():
|
||||
@pytest.mark.parametrize("base_model", ["SVD XT", "Boogu", "Wan Video 3.0", "Flux 3 Video"])
|
||||
def test_new_diffusion_families_route_to_unet(base_model):
|
||||
"""DiT families from CivitAI's official baseModel list that plain
|
||||
"Model"-typed files must not strand on the checkpoint roots."""
|
||||
assert is_diffusion_model_download(
|
||||
"checkpoint", file_types=["Model"], base_model=base_model
|
||||
)
|
||||
|
||||
|
||||
def test_minimax_h3_routes_to_unet():
|
||||
"""CivitAI model 2877206: type "Checkpoint", baseModel "MiniMax H3",
|
||||
all file entries typed "Model"."""
|
||||
assert is_diffusion_model_download(
|
||||
"checkpoint", file_types=["Model"], base_model="MiniMax H3"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"base_model",
|
||||
[
|
||||
"SDXL 1.0",
|
||||
"Illustrious",
|
||||
"SD 1.5",
|
||||
"SD 2.1 768",
|
||||
"SD 3.5 Large",
|
||||
"SDXL Turbo",
|
||||
"Playground v2",
|
||||
"Stable Cascade",
|
||||
],
|
||||
)
|
||||
def test_known_checkpoint_base_models_stay_on_checkpoint_roots(base_model):
|
||||
"""CHECKPOINT_BASE_MODELS members never route to unet, even when the
|
||||
unknown-base-model default is diffusion."""
|
||||
assert not is_diffusion_model_download(
|
||||
"checkpoint", file_types=["Model"], base_model="SDXL 1.0"
|
||||
"checkpoint", file_types=["Model"], base_model=base_model
|
||||
)
|
||||
|
||||
|
||||
def test_unknown_base_model_defaults_to_unet():
|
||||
"""New DiT families appear faster than the allowlist can track them, so
|
||||
unknown baseModels route to unet by default."""
|
||||
assert is_diffusion_model_download(
|
||||
"checkpoint", file_types=["Model"], base_model="Brand New Arch"
|
||||
)
|
||||
|
||||
|
||||
def test_unknown_base_model_honors_checkpoint_setting():
|
||||
assert not is_diffusion_model_download(
|
||||
"checkpoint",
|
||||
file_types=["Model"],
|
||||
base_model="Brand New Arch",
|
||||
unknown_base_model_default="checkpoint",
|
||||
)
|
||||
|
||||
|
||||
def test_unknown_base_model_checkpoint_setting_still_loses_to_file_type():
|
||||
"""The file-type signal stays first regardless of the setting."""
|
||||
assert is_diffusion_model_download(
|
||||
"checkpoint",
|
||||
file_types=["Diffusion Model"],
|
||||
base_model="SDXL 1.0",
|
||||
unknown_base_model_default="checkpoint",
|
||||
)
|
||||
|
||||
|
||||
@@ -35,9 +94,15 @@ def test_non_checkpoint_types_never_route_to_unet():
|
||||
)
|
||||
|
||||
|
||||
def test_empty_inputs_stay_on_checkpoint_roots():
|
||||
assert not is_diffusion_model_download("checkpoint")
|
||||
assert not is_diffusion_model_download("checkpoint", file_types=[], base_model="")
|
||||
def test_empty_base_model_follows_unknown_default():
|
||||
assert is_diffusion_model_download("checkpoint")
|
||||
assert is_diffusion_model_download("checkpoint", file_types=[], base_model="")
|
||||
assert not is_diffusion_model_download(
|
||||
"checkpoint",
|
||||
file_types=[],
|
||||
base_model="",
|
||||
unknown_base_model_default="checkpoint",
|
||||
)
|
||||
|
||||
|
||||
from py.services.download_routing import resolve_other_download_sub_type
|
||||
|
||||
@@ -1300,6 +1300,21 @@ def test_setting_download_skip_base_models_normalizes_string_input(manager):
|
||||
assert manager.get("download_skip_base_models") == ["SDXL 1.0", "Pony"]
|
||||
|
||||
|
||||
def test_unknown_base_model_routing_defaults_to_diffusion_model(manager):
|
||||
assert manager.get("unknown_base_model_routing") == "diffusion_model"
|
||||
|
||||
|
||||
def test_unknown_base_model_routing_is_normalized(manager):
|
||||
manager.set("unknown_base_model_routing", "CHECKPOINT")
|
||||
assert manager.get("unknown_base_model_routing") == "checkpoint"
|
||||
|
||||
manager.set("unknown_base_model_routing", "bogus")
|
||||
assert manager.get("unknown_base_model_routing") == "diffusion_model"
|
||||
|
||||
manager.set("unknown_base_model_routing", None)
|
||||
assert manager.get("unknown_base_model_routing") == "diffusion_model"
|
||||
|
||||
|
||||
def test_skip_previously_downloaded_model_versions_defaults_false(manager):
|
||||
assert manager.get_skip_previously_downloaded_model_versions() is False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user