track explicit search sources and gate local database download visibility
This commit is contained in:
@@ -43,6 +43,80 @@ const renderFormatMethodsSource = fs.readFileSync(
|
||||
'utf8'
|
||||
);
|
||||
|
||||
test('Local Database source ignores installed local model matches before search', () => {
|
||||
const hasMissingSourceSearchAttempt = eval(`(${extractMethod(missingBrowserMethodsSource, 'hasMissingSourceSearchAttempt')})`);
|
||||
const getMissingSourceResultStatus = eval(`(${extractMethod(missingBrowserMethodsSource, 'getMissingSourceResultStatus')})`);
|
||||
const dialog = {
|
||||
hasMissingSourceSearchAttempt,
|
||||
getSearchResultStatusLevel: () => 'exact'
|
||||
};
|
||||
const missing = {
|
||||
matches: [{ confidence: 100, path: 'models/checkpoints/already-installed.safetensors' }],
|
||||
download_source: {
|
||||
source: 'model_list',
|
||||
url: 'https://example.invalid/model.safetensors'
|
||||
}
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
getMissingSourceResultStatus.call(dialog, missing, 'local', {
|
||||
results: { model_list: null, popular: null },
|
||||
explicitSearchSources: [],
|
||||
lastAttemptSources: ['local'],
|
||||
sourceProgress: { local: { status: 'found' } }
|
||||
}),
|
||||
''
|
||||
);
|
||||
});
|
||||
|
||||
test('Local Database source reports its result after Local Database or Everything search', () => {
|
||||
const hasMissingSourceSearchAttempt = eval(`(${extractMethod(missingBrowserMethodsSource, 'hasMissingSourceSearchAttempt')})`);
|
||||
const getMissingSourceResultStatus = eval(`(${extractMethod(missingBrowserMethodsSource, 'getMissingSourceResultStatus')})`);
|
||||
const getSearchResultStatusLevel = eval(`(${extractMethod(missingBrowserMethodsSource, 'getSearchResultStatusLevel')})`);
|
||||
const dialog = { hasMissingSourceSearchAttempt, getSearchResultStatusLevel };
|
||||
|
||||
for (const attemptedSource of ['local', 'all']) {
|
||||
assert.equal(
|
||||
getMissingSourceResultStatus.call(dialog, {}, 'local', {
|
||||
results: {
|
||||
model_list: { source: 'model_list', confidence: 100, match_type: 'exact' },
|
||||
popular: null
|
||||
},
|
||||
explicitSearchSources: [attemptedSource],
|
||||
lastAttemptSources: [],
|
||||
sourceProgress: {}
|
||||
}),
|
||||
'exact'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('automatic Local Database download source stays hidden until that source is searched', () => {
|
||||
const hasMissingSourceSearchAttempt = eval(`(${extractMethod(missingBrowserMethodsSource, 'hasMissingSourceSearchAttempt')})`);
|
||||
const isLocalDatabaseDownloadSource = eval(`(${extractMethod(missingBrowserMethodsSource, 'isLocalDatabaseDownloadSource')})`);
|
||||
const shouldDisplayKnownDownloadSource = eval(`(${extractMethod(missingBrowserMethodsSource, 'shouldDisplayKnownDownloadSource')})`);
|
||||
const state = {
|
||||
selectedSource: 'all',
|
||||
explicitSearchSources: [],
|
||||
lastAttemptSources: [],
|
||||
sourceProgress: {}
|
||||
};
|
||||
const dialog = {
|
||||
searchResultCache: new Map([['missing-key', state]]),
|
||||
getMissingSearchKey: () => 'missing-key',
|
||||
hasMissingSourceSearchAttempt,
|
||||
isLocalDatabaseDownloadSource
|
||||
};
|
||||
const downloadSource = {
|
||||
source: 'model_list',
|
||||
url: 'https://example.invalid/model.safetensors'
|
||||
};
|
||||
|
||||
assert.equal(shouldDisplayKnownDownloadSource.call(dialog, {}, downloadSource, state), false);
|
||||
state.explicitSearchSources = ['local'];
|
||||
assert.equal(shouldDisplayKnownDownloadSource.call(dialog, {}, downloadSource, state), true);
|
||||
});
|
||||
|
||||
test('html template escapes every item in interpolated arrays', () => {
|
||||
const rendered = html`<div>${['<script>', '"quoted"', '&value']}</div>`;
|
||||
|
||||
|
||||
@@ -657,17 +657,6 @@ export const resolveDownloadMethods = {
|
||||
hashMatches
|
||||
)
|
||||
};
|
||||
state.lastAttemptSources = Array.from(new Set([...(state.lastAttemptSources || []), 'local']));
|
||||
state.lastAttemptFound = true;
|
||||
state.sourceProgress = {
|
||||
...(state.sourceProgress || {}),
|
||||
local: {
|
||||
status: 'found',
|
||||
percent: 100,
|
||||
message: 'Hash metadata match',
|
||||
updatedAt: Date.now()
|
||||
}
|
||||
};
|
||||
if (workflowKey) {
|
||||
this.persistSearchStateForWorkflow?.(workflowKey, currentMissing, state);
|
||||
}
|
||||
@@ -788,7 +777,6 @@ export const resolveDownloadMethods = {
|
||||
});
|
||||
|
||||
this.persistLocalMatchesInAnalysisCache(missing, matches);
|
||||
this.persistDownloadedLocalMatchSearchState(missing, matches, metadata);
|
||||
return snapshot;
|
||||
},
|
||||
|
||||
@@ -983,34 +971,6 @@ export const resolveDownloadMethods = {
|
||||
}
|
||||
},
|
||||
|
||||
persistDownloadedLocalMatchSearchState(missing, matches = [], metadata = {}) {
|
||||
if (!missing || !Array.isArray(matches)) return;
|
||||
|
||||
const workflowKey = this.getWorkflowScopedQueueKey?.();
|
||||
if (!workflowKey) return;
|
||||
|
||||
const state = this.getSearchStateForWorkflow?.(workflowKey, missing);
|
||||
if (!state) return;
|
||||
|
||||
const hasRenderableMatch = matches.some(match => Number(match.confidence || 0) >= 70);
|
||||
const hasExactMatch = matches.some(match => Number(match.confidence || 0) >= 100);
|
||||
state.lastAttemptSources = Array.from(new Set([...(state.lastAttemptSources || []), 'local']));
|
||||
state.lastAttemptFound = hasRenderableMatch || state.lastAttemptFound || false;
|
||||
state.lastAttemptError = state.lastAttemptError || null;
|
||||
state.sourceProgress = {
|
||||
...(state.sourceProgress || {}),
|
||||
local: {
|
||||
status: hasRenderableMatch ? 'found' : 'none',
|
||||
percent: 100,
|
||||
message: hasExactMatch ? 'Exact local match' : (hasRenderableMatch ? 'Local match' : 'No local match'),
|
||||
updatedAt: Date.now(),
|
||||
filename: metadata.targetFilename || metadata.filename || ''
|
||||
}
|
||||
};
|
||||
|
||||
this.persistSearchStateForWorkflow?.(workflowKey, missing, state);
|
||||
},
|
||||
|
||||
rememberDownloadUiState(downloadId, info, progress = {}, options = {}) {
|
||||
if (!info?.missing) return null;
|
||||
|
||||
@@ -2394,6 +2354,10 @@ export const resolveDownloadMethods = {
|
||||
};
|
||||
this.backgroundSearchJobs.set(backgroundJobKey, backgroundJob);
|
||||
state.activeSearchRunId = searchRunId;
|
||||
state.explicitSearchSources = Array.from(new Set([
|
||||
...(Array.isArray(state.explicitSearchSources) ? state.explicitSearchSources : []),
|
||||
...sourceIds
|
||||
]));
|
||||
state.lastAttemptSources = sourceIds;
|
||||
state.lastAttemptFound = null;
|
||||
state.lastAttemptError = null;
|
||||
@@ -3007,7 +2971,9 @@ export const resolveDownloadMethods = {
|
||||
match: this.getSearchResultMatchDisplay(result, fallbackLabel, fallbackClass, hashLabel)
|
||||
};
|
||||
};
|
||||
const knownDownloadRow = this.getDownloadSourceTableRow(missing, missing.download_source, hashLabelMap);
|
||||
const knownDownloadRow = this.shouldDisplayKnownDownloadSource(missing, missing.download_source, state)
|
||||
? this.getDownloadSourceTableRow(missing, missing.download_source, hashLabelMap)
|
||||
: null;
|
||||
const hasResults = knownDownloadRow || popular || modelListResult || hfResult || civitaiResult || civarchiveResult || loraManagerArchiveResult || customResults.length;
|
||||
const progressHtml = this.renderSearchProgress(state);
|
||||
const hasActiveProgress = this.hasActiveSearchProgress(state);
|
||||
|
||||
@@ -45,6 +45,7 @@ export const searchPanelMethods = {
|
||||
custom: [],
|
||||
local_hash_matches: []
|
||||
},
|
||||
explicitSearchSources: [],
|
||||
lastAttemptSources: [],
|
||||
lastAttemptBaseModelContext: '',
|
||||
lastAttemptFound: null,
|
||||
|
||||
@@ -467,6 +467,9 @@ export const workflowStateMethods = {
|
||||
clone.selectedSource = clone.selectedSource || 'all';
|
||||
clone.selectedBaseModel = clone.selectedBaseModel || this.getDefaultSearchBaseModel?.() || 'auto';
|
||||
clone.results = this.mergeSearchResults({}, clone.results || {});
|
||||
clone.explicitSearchSources = Array.isArray(clone.explicitSearchSources)
|
||||
? clone.explicitSearchSources
|
||||
: [];
|
||||
clone.lastAttemptSources = Array.isArray(clone.lastAttemptSources)
|
||||
? clone.lastAttemptSources
|
||||
: [];
|
||||
|
||||
@@ -303,20 +303,36 @@ export const missingBrowserMethods = {
|
||||
return 'idle';
|
||||
},
|
||||
|
||||
hasMissingSourceSearchAttempt(missing = {}, source = '', state = null) {
|
||||
const searchState = state || this.searchResultCache?.get(this.getMissingSearchKey(missing));
|
||||
const explicitSources = new Set(
|
||||
Array.isArray(searchState?.explicitSearchSources) ? searchState.explicitSearchSources : []
|
||||
);
|
||||
return explicitSources.has(source) || explicitSources.has('all');
|
||||
},
|
||||
|
||||
isLocalDatabaseDownloadSource(downloadSource = {}) {
|
||||
const source = String(downloadSource?.source || '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/-/g, '_');
|
||||
return source === 'local' || source === 'model_list' || source === 'popular';
|
||||
},
|
||||
|
||||
shouldDisplayKnownDownloadSource(missing = {}, downloadSource = {}, state = null) {
|
||||
if (!downloadSource?.url) return false;
|
||||
if (!this.isLocalDatabaseDownloadSource(downloadSource)) return true;
|
||||
return this.hasMissingSourceSearchAttempt(missing, 'local', state);
|
||||
},
|
||||
|
||||
getMissingSourceResultStatus(missing = {}, source = '', state = null) {
|
||||
const results = state?.results || {};
|
||||
const candidates = [];
|
||||
|
||||
if (source === 'local') {
|
||||
if (!this.hasMissingSourceSearchAttempt(missing, source, state)) return '';
|
||||
|
||||
candidates.push(results.model_list, results.popular);
|
||||
const bestLocalMatch = this.getBestLocalMatch(missing, 70);
|
||||
if (bestLocalMatch) {
|
||||
candidates.push({
|
||||
source: 'local_match',
|
||||
confidence: Number(bestLocalMatch.confidence || 0),
|
||||
match_type: Number(bestLocalMatch.confidence || 0) >= 100 ? 'exact' : 'similar'
|
||||
});
|
||||
}
|
||||
} else if (source === 'huggingface') {
|
||||
candidates.push(results.huggingface);
|
||||
} else if (source === 'civitai') {
|
||||
@@ -2248,7 +2264,7 @@ export const missingBrowserMethods = {
|
||||
const downloadSource = missing.download_source;
|
||||
const urnDownloadId = `urn-download-${missing.node_id}-${missing.widget_index}`;
|
||||
|
||||
if (downloadSource && downloadSource.url) {
|
||||
if (this.shouldDisplayKnownDownloadSource(missing, downloadSource)) {
|
||||
html += this.renderKnownDownloadPanel(missing, downloadSource);
|
||||
} else if (perfectMatches.length > 0) {
|
||||
// Has perfect local match - download not needed, but allow online re-check.
|
||||
|
||||
Reference in New Issue
Block a user