From 3b602a369863ad81663e976a3f95093cf46cbcae Mon Sep 17 00:00:00 2001 From: Will Miao Date: Fri, 22 May 2026 21:03:29 +0800 Subject: [PATCH] feat(lora): add lora_syntax_format setting for syntax version toggle (#917) Adds lora_syntax_format setting (full/legacy) that controls whether syntax uses relative paths (full) or filename only (legacy). Default is legacy for backward compatibility with A1111 convention. The full path format () enables lossless model resolution across subfolders. Ultraworked with Sisyphus (https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- locales/de.json | 8 ++++- locales/en.json | 8 ++++- locales/es.json | 8 ++++- locales/fr.json | 8 ++++- locales/he.json | 8 ++++- locales/ja.json | 8 ++++- locales/ko.json | 8 ++++- locales/ru.json | 8 ++++- locales/zh-CN.json | 8 ++++- locales/zh-TW.json | 8 ++++- py/nodes/lora_loader.py | 3 +- py/nodes/lora_stacker.py | 4 +-- py/nodes/utils.py | 19 +++++++++-- py/services/settings_manager.py | 1 + static/js/state/index.js | 1 + static/js/utils/uiHelpers.js | 8 +++-- .../components/modals/settings_modal.html | 18 ++++++++++- web/comfyui/autocomplete.js | 32 +++++++++++++++++-- 18 files changed, 146 insertions(+), 20 deletions(-) diff --git a/locales/de.json b/locales/de.json index ebbc260d..030d92e7 100644 --- a/locales/de.json +++ b/locales/de.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "Trigger Words in LoRA-Syntax einschließen", - "includeTriggerWordsHelp": "Trainierte Trigger Words beim Kopieren der LoRA-Syntax in die Zwischenablage einschließen" + "includeTriggerWordsHelp": "Trainierte Trigger Words beim Kopieren der LoRA-Syntax in die Zwischenablage einschließen", + "loraSyntaxFormat": "[TODO: Translate] LoRA Syntax Format", + "loraSyntaxFormatHelp": "[TODO: Translate] LoRA syntax format. Full includes subfolder path () for lossless model resolution. Legacy uses filename only () — A1111 convention, may be ambiguous with duplicate filenames across folders.", + "loraSyntaxFormatOptions": { + "full": "[TODO: Translate] Full path (subfolder/name)", + "legacy": "[TODO: Translate] Legacy A1111 (name only)" + } }, "metadataArchive": { "enableArchiveDb": "Metadaten-Archiv-Datenbank aktivieren", diff --git a/locales/en.json b/locales/en.json index b025b9e5..df07ed78 100644 --- a/locales/en.json +++ b/locales/en.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "Include Trigger Words in LoRA Syntax", - "includeTriggerWordsHelp": "Include trained trigger words when copying LoRA syntax to clipboard" + "includeTriggerWordsHelp": "Include trained trigger words when copying LoRA syntax to clipboard", + "loraSyntaxFormat": "LoRA Syntax Format", + "loraSyntaxFormatHelp": "LoRA syntax format. Full includes subfolder path () for lossless model resolution. Legacy uses filename only () — A1111 convention, may be ambiguous with duplicate filenames across folders.", + "loraSyntaxFormatOptions": { + "full": "Full path (subfolder/name)", + "legacy": "Legacy A1111 (name only)" + } }, "metadataArchive": { "enableArchiveDb": "Enable Metadata Archive Database", diff --git a/locales/es.json b/locales/es.json index 36825d67..4ec1c110 100644 --- a/locales/es.json +++ b/locales/es.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "Incluir palabras clave en la sintaxis de LoRA", - "includeTriggerWordsHelp": "Incluir palabras clave entrenadas al copiar la sintaxis de LoRA al portapapeles" + "includeTriggerWordsHelp": "Incluir palabras clave entrenadas al copiar la sintaxis de LoRA al portapapeles", + "loraSyntaxFormat": "[TODO: Translate] LoRA Syntax Format", + "loraSyntaxFormatHelp": "[TODO: Translate] LoRA syntax format. Full includes subfolder path () for lossless model resolution. Legacy uses filename only () — A1111 convention, may be ambiguous with duplicate filenames across folders.", + "loraSyntaxFormatOptions": { + "full": "[TODO: Translate] Full path (subfolder/name)", + "legacy": "[TODO: Translate] Legacy A1111 (name only)" + } }, "metadataArchive": { "enableArchiveDb": "Habilitar base de datos de archivo de metadatos", diff --git a/locales/fr.json b/locales/fr.json index 7a02ffd3..72ce096c 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "Inclure les mots-clés dans la syntaxe LoRA", - "includeTriggerWordsHelp": "Inclure les mots-clés d'entraînement lors de la copie de la syntaxe LoRA dans le presse-papiers" + "includeTriggerWordsHelp": "Inclure les mots-clés d'entraînement lors de la copie de la syntaxe LoRA dans le presse-papiers", + "loraSyntaxFormat": "[TODO: Translate] LoRA Syntax Format", + "loraSyntaxFormatHelp": "[TODO: Translate] LoRA syntax format. Full includes subfolder path () for lossless model resolution. Legacy uses filename only () — A1111 convention, may be ambiguous with duplicate filenames across folders.", + "loraSyntaxFormatOptions": { + "full": "[TODO: Translate] Full path (subfolder/name)", + "legacy": "[TODO: Translate] Legacy A1111 (name only)" + } }, "metadataArchive": { "enableArchiveDb": "Activer la base de données d'archive des métadonnées", diff --git a/locales/he.json b/locales/he.json index 8e7c2a1f..a4206f75 100644 --- a/locales/he.json +++ b/locales/he.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "כלול מילות טריגר בתחביר LoRA", - "includeTriggerWordsHelp": "כלול מילות טריגר מאומנות בעת העתקת תחביר LoRA ללוח" + "includeTriggerWordsHelp": "כלול מילות טריגר מאומנות בעת העתקת תחביר LoRA ללוח", + "loraSyntaxFormat": "[TODO: Translate] LoRA Syntax Format", + "loraSyntaxFormatHelp": "[TODO: Translate] LoRA syntax format. Full includes subfolder path () for lossless model resolution. Legacy uses filename only () — A1111 convention, may be ambiguous with duplicate filenames across folders.", + "loraSyntaxFormatOptions": { + "full": "[TODO: Translate] Full path (subfolder/name)", + "legacy": "[TODO: Translate] Legacy A1111 (name only)" + } }, "metadataArchive": { "enableArchiveDb": "הפעל מסד נתונים של ארכיון מטא-דאטה", diff --git a/locales/ja.json b/locales/ja.json index 45113b29..4c76216f 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "LoRA構文にトリガーワードを含める", - "includeTriggerWordsHelp": "LoRA構文をクリップボードにコピーする際、学習済みトリガーワードを含めます" + "includeTriggerWordsHelp": "LoRA構文をクリップボードにコピーする際、学習済みトリガーワードを含めます", + "loraSyntaxFormat": "[TODO: Translate] LoRA Syntax Format", + "loraSyntaxFormatHelp": "[TODO: Translate] LoRA syntax format. Full includes subfolder path () for lossless model resolution. Legacy uses filename only () — A1111 convention, may be ambiguous with duplicate filenames across folders.", + "loraSyntaxFormatOptions": { + "full": "[TODO: Translate] Full path (subfolder/name)", + "legacy": "[TODO: Translate] Legacy A1111 (name only)" + } }, "metadataArchive": { "enableArchiveDb": "メタデータアーカイブデータベースを有効化", diff --git a/locales/ko.json b/locales/ko.json index ef4db6da..903ea104 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "LoRA 문법에 트리거 단어 포함", - "includeTriggerWordsHelp": "LoRA 문법을 클립보드에 복사할 때 학습된 트리거 단어를 포함합니다" + "includeTriggerWordsHelp": "LoRA 문법을 클립보드에 복사할 때 학습된 트리거 단어를 포함합니다", + "loraSyntaxFormat": "[TODO: Translate] LoRA Syntax Format", + "loraSyntaxFormatHelp": "[TODO: Translate] LoRA syntax format. Full includes subfolder path () for lossless model resolution. Legacy uses filename only () — A1111 convention, may be ambiguous with duplicate filenames across folders.", + "loraSyntaxFormatOptions": { + "full": "[TODO: Translate] Full path (subfolder/name)", + "legacy": "[TODO: Translate] Legacy A1111 (name only)" + } }, "metadataArchive": { "enableArchiveDb": "메타데이터 아카이브 데이터베이스 활성화", diff --git a/locales/ru.json b/locales/ru.json index dfbd4b08..bddeca81 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "Включать триггерные слова в синтаксис LoRA", - "includeTriggerWordsHelp": "Включать обученные триггерные слова при копировании синтаксиса LoRA в буфер обмена" + "includeTriggerWordsHelp": "Включать обученные триггерные слова при копировании синтаксиса LoRA в буфер обмена", + "loraSyntaxFormat": "[TODO: Translate] LoRA Syntax Format", + "loraSyntaxFormatHelp": "[TODO: Translate] LoRA syntax format. Full includes subfolder path () for lossless model resolution. Legacy uses filename only () — A1111 convention, may be ambiguous with duplicate filenames across folders.", + "loraSyntaxFormatOptions": { + "full": "[TODO: Translate] Full path (subfolder/name)", + "legacy": "[TODO: Translate] Legacy A1111 (name only)" + } }, "metadataArchive": { "enableArchiveDb": "Включить архив метаданных", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index e60aadb9..11143c17 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "复制 LoRA 语法时包含触发词", - "includeTriggerWordsHelp": "复制 LoRA 语法到剪贴板时包含训练触发词" + "includeTriggerWordsHelp": "复制 LoRA 语法到剪贴板时包含训练触发词", + "loraSyntaxFormat": "LoRA 语法格式", + "loraSyntaxFormatHelp": "LoRA 语法格式。完整路径(Full)包含子文件夹路径 (),解析精确无歧义。旧版(Legacy)仅使用文件名 ()——A1111 原始约定,同名文件跨文件夹时可能产生歧义。", + "loraSyntaxFormatOptions": { + "full": "完整路径(子文件夹/名称)", + "legacy": "旧版 A1111(仅名称)" + } }, "metadataArchive": { "enableArchiveDb": "启用元数据归档数据库", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index cbe87428..68c1cfbd 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -577,7 +577,13 @@ }, "misc": { "includeTriggerWords": "在 LoRA 語法中包含觸發詞", - "includeTriggerWordsHelp": "複製 LoRA 語法到剪貼簿時包含訓練觸發詞" + "includeTriggerWordsHelp": "複製 LoRA 語法到剪貼簿時包含訓練觸發詞", + "loraSyntaxFormat": "LoRA 語法格式", + "loraSyntaxFormatHelp": "LoRA 語法格式。完整路徑(Full)包含子資料夾路徑 (),解析精確無歧義。舊版(Legacy)僅使用檔名 ()——A1111 原始約定,同名檔案跨資料夾時可能產生歧義。", + "loraSyntaxFormatOptions": { + "full": "完整路徑(子資料夾/名稱)", + "legacy": "舊版 A1111(僅名稱)" + } }, "metadataArchive": { "enableArchiveDb": "啟用中繼資料封存資料庫", diff --git a/py/nodes/lora_loader.py b/py/nodes/lora_loader.py index e97a5893..f91d8090 100644 --- a/py/nodes/lora_loader.py +++ b/py/nodes/lora_loader.py @@ -9,6 +9,7 @@ from ..utils.utils import get_lora_info_absolute from .utils import ( FlexibleOptionalInputType, any_type, + apply_lora_syntax_format, detect_nunchaku_model_kind, extract_lora_name, get_loras_list, @@ -52,7 +53,7 @@ def _collect_widget_entries(kwargs): for lora in get_loras_list(kwargs): if not lora.get("active", False): continue - lora_name = lora["name"] + lora_name = apply_lora_syntax_format(lora["name"]) model_strength = float(lora["strength"]) clip_strength = float(lora.get("clipStrength", model_strength)) lora_path, trigger_words = get_lora_info_absolute(lora_name) diff --git a/py/nodes/lora_stacker.py b/py/nodes/lora_stacker.py index 49a57cac..2b3549a1 100644 --- a/py/nodes/lora_stacker.py +++ b/py/nodes/lora_stacker.py @@ -1,6 +1,6 @@ import os from ..utils.utils import get_lora_info -from .utils import FlexibleOptionalInputType, any_type, extract_lora_name, get_loras_list +from .utils import FlexibleOptionalInputType, any_type, apply_lora_syntax_format, extract_lora_name, get_loras_list import logging @@ -48,7 +48,7 @@ class LoraStackerLM: if not lora.get('active', False): continue - lora_name = lora['name'] + lora_name = apply_lora_syntax_format(lora['name']) model_strength = float(lora['strength']) # Get clip strength - use model strength as default if not specified clip_strength = float(lora.get('clipStrength', model_strength)) diff --git a/py/nodes/utils.py b/py/nodes/utils.py index a78a3ff3..6c6f15eb 100644 --- a/py/nodes/utils.py +++ b/py/nodes/utils.py @@ -44,14 +44,29 @@ import folder_paths # type: ignore logger = logging.getLogger(__name__) +def get_lora_syntax_format(): + try: + from ..services.settings_manager import get_settings_manager + return get_settings_manager().get("lora_syntax_format", "legacy") + except Exception: + return "legacy" + + +def apply_lora_syntax_format(name): + fmt = get_lora_syntax_format() + if fmt == "legacy": + return name.replace("\\", "/").rstrip("/").split("/")[-1] + return name + + def extract_lora_name(lora_path): normalized = lora_path.replace("\\", "/") basename = os.path.basename(normalized) name_no_ext = os.path.splitext(basename)[0] dirname = os.path.dirname(normalized) if dirname and dirname not in (".", "/") and not normalized.startswith("/"): - return f"{dirname}/{name_no_ext}" - return name_no_ext + return apply_lora_syntax_format(f"{dirname}/{name_no_ext}") + return apply_lora_syntax_format(name_no_ext) def get_loras_list(kwargs): diff --git a/py/services/settings_manager.py b/py/services/settings_manager.py index e4fb102d..af7bc764 100644 --- a/py/services/settings_manager.py +++ b/py/services/settings_manager.py @@ -96,6 +96,7 @@ DEFAULT_SETTINGS: Dict[str, Any] = { "compact_mode": False, "priority_tags": DEFAULT_PRIORITY_TAG_CONFIG.copy(), "model_name_display": "model_name", + "lora_syntax_format": "legacy", "model_card_footer_action": "replace_preview", "show_version_on_card": True, "update_flag_strategy": "same_base", diff --git a/static/js/state/index.js b/static/js/state/index.js index 15ead32f..c44b8f80 100644 --- a/static/js/state/index.js +++ b/static/js/state/index.js @@ -37,6 +37,7 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({ card_info_display: 'always', show_folder_sidebar: true, model_name_display: 'model_name', + lora_syntax_format: 'legacy', model_card_footer_action: 'example_images', show_version_on_card: true, include_trigger_words: false, diff --git a/static/js/utils/uiHelpers.js b/static/js/utils/uiHelpers.js index 11f0e8c4..db4c5ea7 100644 --- a/static/js/utils/uiHelpers.js +++ b/static/js/utils/uiHelpers.js @@ -420,12 +420,16 @@ export function getLoraStrengthsFromUsageTips(usageTips = {}) { export function buildLoraSyntax(fileName, usageTips = {}) { const { strength, hasStrength, clipStrength, hasClipStrength } = getLoraStrengthsFromUsageTips(usageTips); + const effectiveName = state.global.settings?.lora_syntax_format === 'legacy' + ? fileName.split('/').pop() + : fileName; + if (hasClipStrength) { const modelStrength = hasStrength ? strength : 1; - return ``; + return ``; } - return ``; + return ``; } export function copyLoraSyntax(card) { diff --git a/templates/components/modals/settings_modal.html b/templates/components/modals/settings_modal.html index cafd7d95..f6bbf73b 100644 --- a/templates/components/modals/settings_modal.html +++ b/templates/components/modals/settings_modal.html @@ -536,7 +536,7 @@ - +
@@ -595,6 +595,22 @@

{{ t('settings.sections.misc') }}

+
+
+
+ +
+
+ +
+
+
diff --git a/web/comfyui/autocomplete.js b/web/comfyui/autocomplete.js index 4f29f2c9..8be8e1b3 100644 --- a/web/comfyui/autocomplete.js +++ b/web/comfyui/autocomplete.js @@ -104,6 +104,30 @@ function removeLoraExtension(fileName = '') { return fileName.replace(/\.(safetensors|ckpt|pt|bin)$/i, ''); } +let _loraSyntaxFormatCache = null; + +function _getLoraSyntaxFormat() { + if (typeof _loraSyntaxFormatCache !== 'undefined' && _loraSyntaxFormatCache !== null) { + return _loraSyntaxFormatCache; + } + return 'legacy'; +} +async function _initLoraSyntaxFormat() { + try { + const response = await api.fetchApi('/lm/settings'); + if (response.ok) { + const data = await response.json(); + if (data.success && data.settings) { + _loraSyntaxFormatCache = data.settings.lora_syntax_format || 'legacy'; + return; + } + } + } catch (e) { + } + _loraSyntaxFormatCache = 'legacy'; +} +_initLoraSyntaxFormat(); + function parseSearchTokens(term = '') { const include = []; const exclude = []; @@ -231,6 +255,10 @@ const MODEL_BEHAVIORS = { const folder = directories.length ? directories.join('/') + '/' : ''; const loraName = folder + baseName; + const resultName = _getLoraSyntaxFormat() === 'legacy' + ? baseName + : loraName; + let strength = 1.0; let hasStrength = false; let clipStrength = null; @@ -265,9 +293,9 @@ const MODEL_BEHAVIORS = { } if (clipStrength !== null) { - return formatAutocompleteInsertion(``); + return formatAutocompleteInsertion(``); } - return formatAutocompleteInsertion(``); + return formatAutocompleteInsertion(``); } }, embeddings: {