feat(lora): support relative paths in <lora:folder/name:strength> syntax (#917)

Autocomplete, copy/send-to-workflow, and recipe syntax now emit
<lora:folder/name:strength> instead of <lora:name:strength>, using
relative paths to disambiguate identically-named loras in different
subfolders without requiring file renames.

Backend: 3-tier hybrid resolution (path → bare → basename fallback)
across get_lora_info, get_lora_info_absolute, get_model_preview_url,
get_model_civitai_url, get_model_info_by_name, get_lora_metadata_by_filename,
and get_hash_by_filename. Also fix get_random_loras and get_cycler_list
to return path-prefixed names for randomizer/cycler consistency.

Frontend: autocomplete, copyLoraSyntax, handleSendToWorkflow emit
folder-prefixed syntax. extract_lora_name preserves relative paths.

Saved image metadata (<lora:...> in EXIF) intentionally keeps basename-only
for compatibility with A1111/Forge ecosystem.
This commit is contained in:
Will Miao
2026-05-20 19:39:12 +08:00
parent 33e5f3d85d
commit 9ce56dd40c
13 changed files with 404 additions and 48 deletions

View File

@@ -226,7 +226,10 @@ const MODEL_BEHAVIORS = {
}
},
async getInsertText(_instance, relativePath) {
const fileName = removeLoraExtension(splitRelativePath(relativePath).fileName);
const { directories, fileName } = splitRelativePath(relativePath);
const baseName = removeLoraExtension(fileName);
const folder = directories.length ? directories.join('/') + '/' : '';
const loraName = folder + baseName;
let strength = 1.0;
let hasStrength = false;
@@ -262,9 +265,9 @@ const MODEL_BEHAVIORS = {
}
if (clipStrength !== null) {
return formatAutocompleteInsertion(`<lora:${fileName}:${strength}:${clipStrength}>`);
return formatAutocompleteInsertion(`<lora:${loraName}:${strength}:${clipStrength}>`);
}
return formatAutocompleteInsertion(`<lora:${fileName}:${strength}>`);
return formatAutocompleteInsertion(`<lora:${loraName}:${strength}>`);
}
},
embeddings: {