fix(downloads): route unknown checkpoint baseModels to diffusion models by default

CivitAI labels new DiT architectures (MiniMax H3, future Flux/Wan/Qwen
variants) as model.type "Checkpoint" with plain "Model" file entries,
so the DIFFUSION_MODEL_BASE_MODELS allowlist could never keep up and
such downloads were mis-routed to the checkpoint roots (e.g. model
2877206 / version 3374439). The set of true full-checkpoint families is
closed, so the baseModel fallback is inverted:

1. file type UNet/Diffusion Model -> unet (unchanged)
2. baseModel in DIFFUSION_MODEL_BASE_MODELS (now incl. MiniMax H3) -> unet
3. baseModel in new CHECKPOINT_BASE_MODELS (SD 1.x/2.x/3.x, SDXL, Pony,
   Illustrious, NoobAI) -> checkpoint
4. unknown/empty baseModel -> new unknown_base_model_routing setting,
   defaulting to diffusion models

The setting is exposed under Settings > Downloads, validated in
SettingsManager, and threaded into both the download manager and the
download routing endpoint so they keep agreeing.
This commit is contained in:
Will Miao
2026-10-02 09:58:01 +08:00
parent 2193ec8f38
commit 3aa32120df
22 changed files with 270 additions and 8 deletions
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "Leer lassen, um aria2c aus dem PATH zu verwenden" "placeholder": "Leer lassen, um aria2c aus dem PATH zu verwenden"
}, },
"aria2HelpLink": "Erfahren Sie, wie Sie das aria2-Download-Backend einrichten", "aria2HelpLink": "Erfahren Sie, wie Sie das aria2-Download-Backend einrichten",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "CivitAI-Host-Einstellung verfügbar", "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.", "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.",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "Leave empty to use aria2c from PATH" "placeholder": "Leave empty to use aria2c from PATH"
}, },
"aria2HelpLink": "Learn how to set up the aria2 download backend", "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 (recommended)",
"checkpoint": "Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "CivitAI host preference available", "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.", "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.",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "Déjalo vacío para usar aria2c desde el PATH" "placeholder": "Déjalo vacío para usar aria2c desde el PATH"
}, },
"aria2HelpLink": "Aprende a configurar el backend de descarga aria2", "aria2HelpLink": "Aprende a configurar el backend de descarga aria2",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "Preferencia de host de CivitAI disponible", "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.", "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.",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "Laisser vide pour utiliser aria2c depuis le PATH" "placeholder": "Laisser vide pour utiliser aria2c depuis le PATH"
}, },
"aria2HelpLink": "Apprenez à configurer le backend de téléchargement aria2", "aria2HelpLink": "Apprenez à configurer le backend de téléchargement aria2",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "Préférence d’hôte CivitAI disponible", "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.", "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.",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "השאר ריק כדי להשתמש ב-aria2c מתוך ה-PATH" "placeholder": "השאר ריק כדי להשתמש ב-aria2c מתוך ה-PATH"
}, },
"aria2HelpLink": "למד כיצד להגדיר את מנוע ההורדה aria2", "aria2HelpLink": "למד כיצד להגדיר את מנוע ההורדה aria2",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "העדפת מארח CivitAI זמינה", "title": "העדפת מארח CivitAI זמינה",
"content": "CivitAI משתמש כעת ב-civitai.com עבור תוכן SFW וב-civitai.red עבור תוכן ללא הגבלות. ניתן לשנות בהגדרות איזה אתר ייפתח כברירת מחדל.", "content": "CivitAI משתמש כעת ב-civitai.com עבור תוכן SFW וב-civitai.red עבור תוכן ללא הגבלות. ניתן לשנות בהגדרות איזה אתר ייפתח כברירת מחדל.",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "空欄のままにすると PATH 上の aria2c を使用します" "placeholder": "空欄のままにすると PATH 上の aria2c を使用します"
}, },
"aria2HelpLink": "aria2 ダウンロードバックエンドの設定方法", "aria2HelpLink": "aria2 ダウンロードバックエンドの設定方法",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "CivitAI ホスト設定を利用できます", "title": "CivitAI ホスト設定を利用できます",
"content": "CivitAI は現在、SFW コンテンツには civitai.com、制限なしコンテンツには civitai.red を使用しています。設定で既定で開くサイトを変更できます。", "content": "CivitAI は現在、SFW コンテンツには civitai.com、制限なしコンテンツには civitai.red を使用しています。設定で既定で開くサイトを変更できます。",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "비워 두면 PATH의 aria2c를 사용합니다" "placeholder": "비워 두면 PATH의 aria2c를 사용합니다"
}, },
"aria2HelpLink": "aria2 다운로드 백엔드 설정 방법 알아보기", "aria2HelpLink": "aria2 다운로드 백엔드 설정 방법 알아보기",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "CivitAI 호스트 기본 설정 사용 가능", "title": "CivitAI 호스트 기본 설정 사용 가능",
"content": "이제 CivitAI는 SFW 콘텐츠에 civitai.com을, 무제한 콘텐츠에 civitai.red를 사용합니다. 설정에서 기본으로 열 사이트를 변경할 수 있습니다.", "content": "이제 CivitAI는 SFW 콘텐츠에 civitai.com을, 무제한 콘텐츠에 civitai.red를 사용합니다. 설정에서 기본으로 열 사이트를 변경할 수 있습니다.",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "Оставьте пустым, чтобы использовать aria2c из PATH" "placeholder": "Оставьте пустым, чтобы использовать aria2c из PATH"
}, },
"aria2HelpLink": "Узнайте, как настроить сервер загрузки aria2", "aria2HelpLink": "Узнайте, как настроить сервер загрузки aria2",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "Доступна настройка хоста CivitAI", "title": "Доступна настройка хоста CivitAI",
"content": "Теперь CivitAI использует civitai.com для контента SFW и civitai.red для контента без ограничений. В настройках можно изменить, какой сайт открывать по умолчанию.", "content": "Теперь CivitAI использует civitai.com для контента SFW и civitai.red для контента без ограничений. В настройках можно изменить, какой сайт открывать по умолчанию.",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "留空则使用 PATH 中的 aria2c" "placeholder": "留空则使用 PATH 中的 aria2c"
}, },
"aria2HelpLink": "了解如何配置 aria2 下载后端", "aria2HelpLink": "了解如何配置 aria2 下载后端",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "已提供 CivitAI 站点偏好设置", "title": "已提供 CivitAI 站点偏好设置",
"content": "CivitAI 现在使用 civitai.com 提供 SFW 内容,使用 civitai.red 提供无限制内容。你可以在设置中更改默认打开的站点。", "content": "CivitAI 现在使用 civitai.com 提供 SFW 内容,使用 civitai.red 提供无限制内容。你可以在设置中更改默认打开的站点。",
+8
View File
@@ -353,6 +353,14 @@
"placeholder": "留空則使用 PATH 中的 aria2c" "placeholder": "留空則使用 PATH 中的 aria2c"
}, },
"aria2HelpLink": "了解如何設定 aria2 下載後端", "aria2HelpLink": "了解如何設定 aria2 下載後端",
"unknownBaseModelRouting": {
"label": "[TODO: Translate] Unknown base model routing",
"help": "[TODO: Translate] 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": "[TODO: Translate] Diffusion models (recommended)",
"checkpoint": "[TODO: Translate] Checkpoints"
}
},
"civitaiHostBanner": { "civitaiHostBanner": {
"title": "已提供 CivitAI 站點偏好設定", "title": "已提供 CivitAI 站點偏好設定",
"content": "CivitAI 現在使用 civitai.com 提供 SFW 內容,使用 civitai.red 提供無限制內容。您可以在設定中變更預設開啟的站點。", "content": "CivitAI 現在使用 civitai.com 提供 SFW 內容,使用 civitai.red 提供無限制內容。您可以在設定中變更預設開啟的站點。",
@@ -96,10 +96,15 @@ class DownloadRoutingHandler:
} }
) )
from ...services.settings_manager import get_settings_manager
is_diffusion = is_diffusion_model_download( is_diffusion = is_diffusion_model_download(
model_type, model_type,
file_types=(str(t) for t in file_types), file_types=(str(t) for t in file_types),
base_model=base_model, base_model=base_model,
unknown_base_model_default=get_settings_manager().get(
"unknown_base_model_routing", "diffusion_model"
),
) )
return web.json_response( return web.json_response(
{ {
+3
View File
@@ -1670,6 +1670,9 @@ class DownloadManager:
model_type, model_type,
file_types=(f.get("type", "") for f in version_info.get("files", [])), file_types=(f.get("type", "") for f in version_info.get("files", [])),
base_model=base_model_value, 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): # Existence check after the metadata fetch (#1058):
+22 -2
View File
@@ -13,6 +13,7 @@ import logging
from typing import Iterable, Optional from typing import Iterable, Optional
from ..utils.constants import ( from ..utils.constants import (
CHECKPOINT_BASE_MODELS,
CIVITAI_FILE_TYPE_TO_OTHER_SUB_TYPE, CIVITAI_FILE_TYPE_TO_OTHER_SUB_TYPE,
CIVITAI_TYPE_TO_OTHER_SUB_TYPE, CIVITAI_TYPE_TO_OTHER_SUB_TYPE,
DIFFUSION_MODEL_BASE_MODELS, DIFFUSION_MODEL_BASE_MODELS,
@@ -24,17 +25,27 @@ logger = logging.getLogger(__name__)
# model (loaded via UNETLoader in ComfyUI) rather than a full checkpoint. # model (loaded via UNETLoader in ComfyUI) rather than a full checkpoint.
DIFFUSION_FILE_TYPES = frozenset({"UNet", "Diffusion Model"}) 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( def is_diffusion_model_download(
model_type: str, model_type: str,
file_types: Iterable[str] = (), file_types: Iterable[str] = (),
base_model: str = "", base_model: str = "",
unknown_base_model_default: str = ROUTING_DIFFUSION_MODEL,
) -> bool: ) -> bool:
"""Return True when a download should be routed to the unet roots. """Return True when a download should be routed to the unet roots.
Only applies to downloads initiated from the checkpoint library. Only applies to downloads initiated from the checkpoint library.
Priority: (1) any file has type "UNet" or "Diffusion Model" (the more 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": if model_type != "checkpoint":
return False return False
@@ -54,7 +65,16 @@ def is_diffusion_model_download(
) )
return True 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( def resolve_other_download_sub_type(
+15
View File
@@ -130,6 +130,9 @@ DEFAULT_SETTINGS: Dict[str, Any] = {
"metadata_refresh_skip_paths": [], "metadata_refresh_skip_paths": [],
"skip_previously_downloaded_model_versions": False, "skip_previously_downloaded_model_versions": False,
"download_skip_base_models": [], "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_auto_enabled": True,
"backup_retention_count": 5, "backup_retention_count": 5,
"use_new_license_icons": True, "use_new_license_icons": True,
@@ -1628,6 +1631,16 @@ class SettingsManager:
return normalized return normalized
return "alongside" 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: def _refresh_sidecar_storage_config(self) -> None:
"""Rebuild dependent config state after sidecar storage settings change.""" """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) self._migrate_recipes_directory(current_recipes_dir, target_recipes_dir)
elif key == "sidecar_storage_mode": elif key == "sidecar_storage_mode":
value = self._normalize_sidecar_storage_mode(value) 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": elif key == "sidecar_storage_path":
value = self._normalize_recipes_path_value(value) value = self._normalize_recipes_path_value(value)
self.settings[key] = value self.settings[key] = value
+34
View File
@@ -371,6 +371,7 @@ DIFFUSION_MODEL_BASE_MODELS = frozenset(
# Other diffusion models # Other diffusion models
"Ernie", "Ernie",
"Ernie Turbo", "Ernie Turbo",
"MiniMax H3",
"Nucleus", "Nucleus",
"Qwen", "Qwen",
"ZImageBase", "ZImageBase",
@@ -380,6 +381,39 @@ 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).
# "Pony V7" is deliberately excluded: it is not an SDXL-derivative full
# checkpoint, 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.1",
# 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 1.0",
"SDXL Lightning",
"SDXL Hyper",
"Pony",
"Illustrious",
"NoobAI",
]
)
# Supported baseModel values for download exclusion settings. # Supported baseModel values for download exclusion settings.
# Keep this aligned with static/js/utils/constants.js, excluding the generic "Other" value. # Keep this aligned with static/js/utils/constants.js, excluding the generic "Other" value.
SUPPORTED_DOWNLOAD_SKIP_BASE_MODELS = frozenset( SUPPORTED_DOWNLOAD_SKIP_BASE_MODELS = frozenset(
+5
View File
@@ -1053,6 +1053,11 @@ export class SettingsManager {
downloadBackendSelect.value = state.global.settings.download_backend || 'python'; 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'); const aria2cPathInput = document.getElementById('aria2cPath');
if (aria2cPathInput) { if (aria2cPathInput) {
aria2cPathInput.value = state.global.settings.aria2c_path || ''; aria2cPathInput.value = state.global.settings.aria2c_path || '';
+1
View File
@@ -60,6 +60,7 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({
metadata_refresh_skip_paths: [], metadata_refresh_skip_paths: [],
skip_previously_downloaded_model_versions: false, skip_previously_downloaded_model_versions: false,
download_skip_base_models: [], download_skip_base_models: [],
unknown_base_model_routing: 'diffusion_model',
backup_auto_enabled: true, backup_auto_enabled: true,
backup_retention_count: 5, backup_retention_count: 5,
sidecar_storage_mode: 'alongside', sidecar_storage_mode: 'alongside',
@@ -146,6 +146,10 @@
</div> </div>
</div> </div>
</div> </div>
{{ 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') }}
</div> </div>
<!-- AI Provider Configuration (BYOK) --> <!-- AI Provider Configuration (BYOK) -->
@@ -73,6 +73,42 @@ async def test_regular_checkpoint_stays_on_checkpoint_root():
assert payload["root_kind"] == "checkpoint" 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 @pytest.mark.asyncio
async def test_lora_is_never_diffusion(): async def test_lora_is_never_diffusion():
handler = DownloadRoutingHandler() handler = DownloadRoutingHandler()
@@ -2209,7 +2209,7 @@ async def test_download_falls_back_to_rest_name_when_mini_fails(
metadata_provider.payload = { metadata_provider.payload = {
"id": 42, "id": 42,
"model": {"type": "Checkpoint", "tags": ["fantasy"]}, "model": {"type": "Checkpoint", "tags": ["fantasy"]},
"baseModel": "BaseModel", "baseModel": "SDXL 1.0",
"creator": {"username": "Author"}, "creator": {"username": "Author"},
"files": [ "files": [
{ {
+49 -5
View File
@@ -20,9 +20,47 @@ def test_base_model_fallback_routes_to_unet():
) )
def test_regular_checkpoint_stays_on_checkpoint_roots(): 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 3.5 Large"])
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( 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 +73,15 @@ def test_non_checkpoint_types_never_route_to_unet():
) )
def test_empty_inputs_stay_on_checkpoint_roots(): def test_empty_base_model_follows_unknown_default():
assert not is_diffusion_model_download("checkpoint") assert is_diffusion_model_download("checkpoint")
assert not is_diffusion_model_download("checkpoint", file_types=[], base_model="") 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 from py.services.download_routing import resolve_other_download_sub_type
+15
View File
@@ -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"] 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): def test_skip_previously_downloaded_model_versions_defaults_false(manager):
assert manager.get_skip_previously_downloaded_model_versions() is False assert manager.get_skip_previously_downloaded_model_versions() is False