refactor: unify settings schema and dynamic node rules exposure across backend and frontend
This commit is contained in:
+8
-2
@@ -4724,12 +4724,14 @@ class ModelResolverExtension:
|
||||
@json_api_endpoint("capabilities")
|
||||
async def get_capabilities(request):
|
||||
"""Get optional source capabilities available in this install."""
|
||||
from .core.workflow_analyzer import NODE_TYPE_MODEL_WIDGET_CATEGORIES
|
||||
return web.json_response(
|
||||
{
|
||||
"sources": {
|
||||
"civarchive": is_civarchive_available(),
|
||||
"lora_manager_archive": is_lora_manager_archive_available()
|
||||
}
|
||||
},
|
||||
"node_rules": NODE_TYPE_MODEL_WIDGET_CATEGORIES
|
||||
}
|
||||
)
|
||||
|
||||
@@ -5011,7 +5013,11 @@ class ModelResolverExtension:
|
||||
async def get_settings_route(request):
|
||||
"""Return persisted settings (API keys, preferences)."""
|
||||
data = await asyncio.to_thread(load_resolver_settings)
|
||||
return web.json_response(data)
|
||||
from .core.settings import get_settings_schema
|
||||
return web.json_response({
|
||||
"settings": data,
|
||||
"schema": get_settings_schema()
|
||||
})
|
||||
|
||||
@routes.post("/model_resolver/settings")
|
||||
@json_api_endpoint("settings POST")
|
||||
|
||||
@@ -73,10 +73,13 @@ def _extract_size_from_object(data: Any, prefix: str = "") -> Optional[Tuple[int
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
|
||||
for field_path, value, multiplier in _iter_size_fields(data, prefix):
|
||||
size = _coerce_size_bytes(value, multiplier=multiplier)
|
||||
if size is not None:
|
||||
return size, field_path
|
||||
from .type_utils import extract_file_size
|
||||
size = extract_file_size(data)
|
||||
if size is not None:
|
||||
for field_path, value, multiplier in _iter_size_fields(data, prefix):
|
||||
if _coerce_size_bytes(value, multiplier=multiplier) == size:
|
||||
return size, field_path
|
||||
return size, f"{prefix}size"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -16,6 +16,35 @@ from .type_utils import normalize_download_category, to_bool
|
||||
DOWNLOAD_PATH_MODES = {"suggested", "template", "manual"}
|
||||
DOWNLOAD_BACKENDS = {"python", "aria2"}
|
||||
|
||||
SETTINGS_SCHEMA: List[Dict[str, Any]] = [
|
||||
{ "serverKey": "civitai_key", "localKey": "ModelResolver.civitaiApiKey", "type": "string", "default": "" },
|
||||
{ "serverKey": "civitai_session_token", "localKey": "ModelResolver.civitaiSessionToken", "type": "string", "default": "" },
|
||||
{ "serverKey": "civitai_use_trpc_search", "localKey": "ModelResolver.civitaiUseTrpcSearch", "type": "boolean", "default": True },
|
||||
{ "serverKey": "civitai_use_api_search", "localKey": "ModelResolver.civitaiUseApiSearch", "type": "boolean", "default": True },
|
||||
{ "serverKey": "civitai_use_html_fallback", "localKey": "ModelResolver.civitaiUseHtmlFallback", "type": "boolean", "default": True },
|
||||
{ "serverKey": "hf_token", "localKey": "ModelResolver.huggingFaceToken", "type": "string", "default": "" },
|
||||
{ "serverKey": "brave_search_api_key", "localKey": "ModelResolver.braveSearchApiKey", "type": "string", "default": "" },
|
||||
{ "serverKey": "hf_use_api_search", "localKey": "ModelResolver.hfUseApiSearch", "type": "boolean", "default": True },
|
||||
{ "serverKey": "hf_use_comfy_org_fallback", "localKey": "ModelResolver.hfUseComfyOrgFallback", "type": "boolean", "default": True },
|
||||
{ "serverKey": "hf_use_brave_fallback", "localKey": "ModelResolver.hfUseBraveFallback", "type": "boolean", "default": True },
|
||||
{ "serverKey": "auto_fill_base_model", "localKey": "ModelResolver.autoFillBaseModel", "type": "boolean", "default": True },
|
||||
{ "serverKey": "auto_fill_subfolder", "localKey": "ModelResolver.autoFillSubfolder", "type": "boolean", "default": True },
|
||||
{ "serverKey": "auto_refresh_comfy_models_after_apply", "localKey": "ModelResolver.autoRefreshComfyModelsAfterApply", "type": "boolean", "default": True },
|
||||
{ "serverKey": "workflow_hash_metadata_enabled", "localKey": "ModelResolver.workflowHashMetadataEnabled", "type": "boolean", "default": True },
|
||||
{ "serverKey": "workflow_dependency_marker_enabled", "localKey": "ModelResolver.workflowDependencyMarkerEnabled", "type": "boolean", "default": False },
|
||||
{ "serverKey": "download_backend", "localKey": "ModelResolver.downloadBackend", "type": "backend", "default": "python" },
|
||||
{ "serverKey": "aria2c_path", "localKey": "ModelResolver.aria2cPath", "type": "string", "default": "" },
|
||||
{ "serverKey": "aria2_auto_stop_daemon", "localKey": "ModelResolver.aria2AutoStopDaemon", "type": "boolean", "default": True },
|
||||
{ "serverKey": "download_path_mode", "localKey": "ModelResolver.downloadPathMode", "type": "pathMode", "default": "suggested" },
|
||||
{ "serverKey": "download_path_templates", "localKey": "ModelResolver.downloadPathTemplates", "type": "json", "default": {} },
|
||||
{ "serverKey": "base_model_path_mappings", "localKey": "ModelResolver.baseModelPathMappings", "type": "json", "default": {} },
|
||||
{ "serverKey": "frontend_logs_enabled", "localKey": "ModelResolver.frontendLogsEnabled", "type": "frontendLogsEnabled", "default": True },
|
||||
{ "serverKey": "backend_logs_enabled", "localKey": "ModelResolver.backendLogsEnabled", "type": "backendLogsEnabled", "default": True },
|
||||
{ "serverKey": "frontend_log_level", "localKey": "ModelResolver.frontendLogLevel", "type": "string", "default": "ERROR" },
|
||||
{ "serverKey": "backend_log_level", "localKey": "ModelResolver.backendLogLevel", "type": "string", "default": "ERROR" },
|
||||
{ "serverKey": "civitai_candidate_limit", "localKey": "ModelResolver.civitaiCandidateLimit", "type": "candidateLimit", "default": 5 },
|
||||
]
|
||||
|
||||
DEFAULT_DOWNLOAD_PATH_TEMPLATES: Dict[str, str] = {
|
||||
"loras": "{base_model}/{first_tag}",
|
||||
"checkpoints": "{base_model}",
|
||||
@@ -358,3 +387,8 @@ def get_default_root_for_category(
|
||||
if not key:
|
||||
return ""
|
||||
return str(active_settings.get(key) or "").strip()
|
||||
|
||||
|
||||
def get_settings_schema() -> List[Dict[str, Any]]:
|
||||
"""Return the schema dictionary for frontend setting options synchronization."""
|
||||
return SETTINGS_SCHEMA
|
||||
|
||||
@@ -281,6 +281,16 @@ export const downloadTargetMethods = {
|
||||
|
||||
getExactNodeTypeDownloadCategory(nodeType = '') {
|
||||
const normalizedNodeType = String(nodeType || '').trim();
|
||||
const dynamicRules = this.capabilities?.node_rules;
|
||||
if (dynamicRules && typeof dynamicRules === 'object' && dynamicRules[normalizedNodeType]) {
|
||||
const widgetsMap = dynamicRules[normalizedNodeType];
|
||||
if (widgetsMap && typeof widgetsMap === 'object') {
|
||||
const indices = Object.keys(widgetsMap).map(k => parseInt(k, 10)).sort((a, b) => a - b);
|
||||
if (indices.length > 0) {
|
||||
return widgetsMap[indices[0]] || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
const exactMap = {
|
||||
CheckpointLoaderSimple: 'checkpoints',
|
||||
CheckpointLoader: 'checkpoints',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app } from "../../../../../scripts/app.js";
|
||||
import { app } from "../../../../../scripts/app.js";
|
||||
import { api } from "../../../../../scripts/api.js";
|
||||
import { $el } from "../../../../../scripts/ui.js";
|
||||
import { createModuleLogger } from "../../log_system/log_funcs.js";
|
||||
@@ -67,7 +67,7 @@ export const workflowStateMethods = {
|
||||
'on'
|
||||
]);
|
||||
const loraStrengthNodeTypes = new Set(['LoraLoader', 'LoraLoaderModelOnly']);
|
||||
const modelWidgetIndicesByNodeType = {
|
||||
const staticFallback = {
|
||||
CheckpointLoaderSimple: [0],
|
||||
CheckpointLoader: [1],
|
||||
DiffusersLoader: [0],
|
||||
@@ -131,6 +131,16 @@ export const workflowStateMethods = {
|
||||
LTXAVTextEncoderLoader: [0, 1]
|
||||
};
|
||||
|
||||
const modelWidgetIndicesByNodeType = { ...staticFallback };
|
||||
const dynamicRules = this.capabilities?.node_rules;
|
||||
if (dynamicRules && typeof dynamicRules === 'object') {
|
||||
for (const [nodeType, widgetsMap] of Object.entries(dynamicRules)) {
|
||||
if (widgetsMap && typeof widgetsMap === 'object') {
|
||||
modelWidgetIndicesByNodeType[nodeType] = Object.keys(widgetsMap).map(k => parseInt(k, 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeKey = (key = '') => String(key).replace(/[-\s]/g, '_').toLowerCase();
|
||||
const isModelWidgetIndex = (nodeType = '', index = -1) => (
|
||||
modelWidgetIndicesByNodeType[nodeType] || []
|
||||
|
||||
@@ -3572,8 +3572,15 @@ export const optionsMethods = {
|
||||
return;
|
||||
}
|
||||
|
||||
let settingsObj = data;
|
||||
if (data.schema && Array.isArray(data.schema)) {
|
||||
SETTINGS_MAP.length = 0;
|
||||
SETTINGS_MAP.push(...data.schema);
|
||||
settingsObj = data.settings || {};
|
||||
}
|
||||
|
||||
for (const opt of SETTINGS_MAP) {
|
||||
const serverValue = data[opt.serverKey];
|
||||
const serverValue = settingsObj[opt.serverKey];
|
||||
if (serverValue === undefined) continue;
|
||||
|
||||
if (opt.type === 'boolean' || opt.type === 'frontendLogsEnabled' || opt.type === 'backendLogsEnabled') {
|
||||
@@ -3591,14 +3598,14 @@ export const optionsMethods = {
|
||||
}
|
||||
}
|
||||
this.getDefaultRootCategoryDefinitions().forEach((item) => {
|
||||
if (data[item.settingKey] !== undefined) {
|
||||
safeStorage.setItem(item.storageKey, String(data[item.settingKey] || ''));
|
||||
if (settingsObj[item.settingKey] !== undefined) {
|
||||
safeStorage.setItem(item.storageKey, String(settingsObj[item.settingKey] || ''));
|
||||
}
|
||||
});
|
||||
|
||||
// Source-enabled flags stored as a nested object
|
||||
if (data.search_source_enabled && typeof data.search_source_enabled === 'object') {
|
||||
Object.entries(data.search_source_enabled).forEach(([key, val]) => {
|
||||
if (settingsObj.search_source_enabled && typeof settingsObj.search_source_enabled === 'object') {
|
||||
Object.entries(settingsObj.search_source_enabled).forEach(([key, val]) => {
|
||||
if (key) safeStorage.setItem(key, val ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user