From ea3b987d6ac634552ba79dbf6997ae6dd57475e5 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Tue, 25 Aug 2026 04:36:37 +0200 Subject: [PATCH] frontend: prefer workflow base model over incompatible archive metadata in auto mode --- tests/test_downloads_tab_workflow_route.mjs | 74 +++++++++++++++++++ .../search/download_target_methods.js | 9 +++ 2 files changed, 83 insertions(+) diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index 37db101..083dd63 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -4825,6 +4825,80 @@ test('search suggestion metadata prefers exact matching base model over weaker a assert.equal(merged.filename, 'snofs_krea_v1.safetensors'); }); +test('automatic workflow base model filters incompatible archive metadata', () => { + const getCachedSearchSuggestionData = eval(`(${extractMethod(downloadTargetMethodsSource, 'getCachedSearchSuggestionData')})`); + const getFirstSearchResult = eval(`(${extractMethod(downloadTargetMethodsSource, 'getFirstSearchResult')})`); + const getSearchSuggestionPreferredBaseModel = eval(`(${extractMethod(downloadTargetMethodsSource, 'getSearchSuggestionPreferredBaseModel')})`); + const baseModelMatchesSearchSuggestionPreference = eval(`(${extractMethod(downloadTargetMethodsSource, 'baseModelMatchesSearchSuggestionPreference')})`); + const getSearchSuggestionResultScore = eval(`(${extractMethod(downloadTargetMethodsSource, 'getSearchSuggestionResultScore')})`); + const state = { + selectedBaseModel: 'auto', + results: { + civitai: { + base_model: 'Krea 2', + tags: ['concept'], + filename: 'Text_Refusal_Reduction_Krea2.safetensors', + name: 'Krea2 TextFusion Refusal-Reduction LoRA v1.0', + match_type: 'model_title' + }, + civarchive: { + base_model: 'SDXL 1.0', + tags: ['concept'], + filename: 'text.safetensors', + name: 'Text Aid SDXL v1.0', + match_type: 'similar', + confidence: 85 + } + } + }; + const dialog = { + searchResultCache: new Map([['missing-key', state]]), + getCachedSearchSuggestionData, + getFirstSearchResult, + getSearchSuggestionPreferredBaseModel, + baseModelMatchesSearchSuggestionPreference, + getSearchSuggestionResultScore, + getMissingSearchKey() { + return 'missing-key'; + }, + getMissingLocalBaseModel() { + return ''; + }, + getSavedDownloadTargetSelection() { + return null; + }, + getBaseModelIndependentSearchType() { + return ''; + }, + getDominantWorkflowBaseModel() { + return 'Krea 2'; + }, + normalizeBaseModelToken(value = '') { + return String(value || '').toLowerCase().replace(/[^a-z0-9]+/g, ''); + }, + resolveBaseModelAlias(value = '') { + const token = this.normalizeBaseModelToken(value); + if (token === 'krea2') return 'Krea 2'; + if (token === 'sdxl10') return 'SDXL 1.0'; + return ''; + }, + resolveBaseModelAliasExact(value = '') { + return this.resolveBaseModelAlias(value); + }, + getSourceResultDownloadCategory() { + return 'loras'; + } + }; + + const missing = { category: 'loras' }; + assert.equal(getSearchSuggestionPreferredBaseModel.call(dialog, missing), 'Krea 2'); + + const merged = getCachedSearchSuggestionData.call(dialog, missing); + assert.equal(merged.base_model, 'Krea 2'); + assert.equal(merged.name, 'Krea2 TextFusion Refusal-Reduction LoRA v1.0'); + assert.equal(merged.filename, 'Text_Refusal_Reduction_Krea2.safetensors'); +}); + test('download path metadata preserves source precedence and workflow fallbacks', () => { const getDownloadSourceContext = eval(`(${extractMethod(downloadTargetMethodsSource, 'getDownloadSourceContext')})`); const getDownloadPathMetadata = eval(`(${extractMethod(downloadTargetMethodsSource, 'getDownloadPathMetadata')})`); diff --git a/web/resolver/search/download_target_methods.js b/web/resolver/search/download_target_methods.js index aa60f63..0b00147 100644 --- a/web/resolver/search/download_target_methods.js +++ b/web/resolver/search/download_target_methods.js @@ -1540,6 +1540,7 @@ export const downloadTargetMethods = { getSearchSuggestionPreferredBaseModel(missing = {}) { const state = this.searchResultCache?.get(this.getMissingSearchKey?.(missing)); const selectedBaseModel = state?.selectedBaseModel || ''; + const usesAutomaticBaseModel = !selectedBaseModel || selectedBaseModel === 'auto'; const candidates = []; const addCandidate = (value) => { if (value !== undefined && value !== null && String(value).trim()) { @@ -1562,6 +1563,14 @@ export const downloadTargetMethods = { addCandidate(this.resolveBaseModelAliasFromPath?.(missing?.original_path)); addCandidate(this.resolveBaseModelAliasFromPath?.(missing?.name)); + // Keep search-result metadata aligned with the model shown by the + // Auto selector. Without this fallback, a high-confidence result from + // another base model could supply the subfolder template metadata + // before the compatible result is considered. + if (usesAutomaticBaseModel && !this.getBaseModelIndependentSearchType?.(missing)) { + addCandidate(this.getDominantWorkflowBaseModel?.()); + } + for (const candidate of candidates) { const canonical = this.resolveBaseModelAlias?.(candidate) || this.resolveBaseModelAliasExact?.(candidate)