Prefer HuggingFace direct download URLs and preserve them through selection
This commit is contained in:
@@ -3830,6 +3830,39 @@ test('automatic Local Database download source stays hidden until that source is
|
||||
assert.equal(shouldDisplayKnownDownloadSource.call(dialog, {}, downloadSource, state), true);
|
||||
});
|
||||
|
||||
test('HuggingFace fallback download source prefers the direct file URL', () => {
|
||||
const getBestDownloadSourceForMissing = eval(`(${extractMethod(resolveDownloadMethodsSource, 'getBestDownloadSourceForMissing')})`);
|
||||
const pageUrl = 'https://huggingface.co/Comfy-Org/YuE2/tree/main/checkpoints';
|
||||
const downloadUrl = 'https://huggingface.co/Comfy-Org/YuE2/resolve/main/checkpoints/yue2_3b_bf16.safetensors';
|
||||
const missing = {
|
||||
missing_search_key: 'missing-key',
|
||||
original_path: 'yue2_convrot_int8.safetensors',
|
||||
category: 'checkpoints',
|
||||
};
|
||||
const dialog = {
|
||||
searchResultCache: new Map([[
|
||||
'missing-key',
|
||||
{ results: { huggingface: {
|
||||
source: 'huggingface',
|
||||
model_id: 'Comfy-Org/YuE2',
|
||||
filename: 'yue2_3b_bf16.safetensors',
|
||||
url: pageUrl,
|
||||
download_url: downloadUrl,
|
||||
} } }
|
||||
]]),
|
||||
getMissingSearchKey() {
|
||||
return 'missing-key';
|
||||
},
|
||||
getFilenameFromPath(value = '') {
|
||||
return String(value).split(/[\\/]/).at(-1) || '';
|
||||
},
|
||||
};
|
||||
|
||||
const source = getBestDownloadSourceForMissing.call(dialog, missing);
|
||||
|
||||
assert.equal(source.url, downloadUrl);
|
||||
});
|
||||
|
||||
test('inactive missing models require every workflow reference to be inactive', () => {
|
||||
const { isMissingModelInactive } = missingModelStateMethods;
|
||||
|
||||
@@ -8420,6 +8453,66 @@ test('applying source model details selection updates current download source ha
|
||||
assert.equal(refreshCalls.length, 1);
|
||||
});
|
||||
|
||||
test('applying a HuggingFace file selection keeps the direct URL in the search result', () => {
|
||||
const applySourceModelDetailsSelection = eval(`(${extractMethod(modelInfoMethodsSource, 'applySourceModelDetailsSelection')})`);
|
||||
const sha256 = 'c'.repeat(64);
|
||||
const pageUrl = 'https://huggingface.co/Comfy-Org/YuE2/tree/main/checkpoints';
|
||||
const downloadUrl = 'https://huggingface.co/Comfy-Org/YuE2/resolve/main/checkpoints/yue2_3b_bf16.safetensors';
|
||||
const missing = {
|
||||
original_path: 'yue2_convrot_int8.safetensors',
|
||||
category: 'checkpoints',
|
||||
civitai_info: {},
|
||||
};
|
||||
const state = { results: {} };
|
||||
const dialog = {
|
||||
getMissingByKey(key) {
|
||||
return key === 'missing-key' ? missing : null;
|
||||
},
|
||||
getSearchState() {
|
||||
return state;
|
||||
},
|
||||
getSourceModelFileHash(value = {}) {
|
||||
const hashes = value.hashes && typeof value.hashes === 'object' ? value.hashes : {};
|
||||
return String(value.sha256 || value.hash || hashes.SHA256 || hashes.sha256 || '').trim().toLowerCase();
|
||||
},
|
||||
refreshSearchUiForMissing() {},
|
||||
refreshSearchBaseModelLabels() {},
|
||||
updateBatchFooterButtons() {},
|
||||
persistSearchStateForActiveWorkflow() {},
|
||||
syncRemoteHashMatchesForResult() {},
|
||||
showNotification() {},
|
||||
};
|
||||
|
||||
applySourceModelDetailsSelection.call(dialog, {
|
||||
source: 'huggingface',
|
||||
model_id: 'Comfy-Org/YuE2',
|
||||
version_id: 'main',
|
||||
name: 'Comfy-Org/YuE2',
|
||||
filename: 'yue2_3b_bf16.safetensors',
|
||||
url: pageUrl,
|
||||
download_url: downloadUrl,
|
||||
path: 'checkpoints/yue2_3b_bf16.safetensors',
|
||||
sha256,
|
||||
hashes: { SHA256: sha256 },
|
||||
file_info: {
|
||||
name: 'yue2_3b_bf16.safetensors',
|
||||
path: 'checkpoints/yue2_3b_bf16.safetensors',
|
||||
sha256,
|
||||
hashes: { SHA256: sha256 },
|
||||
},
|
||||
}, {
|
||||
missing_key: 'missing-key',
|
||||
details_source: 'huggingface',
|
||||
});
|
||||
|
||||
assert.equal(state.results.huggingface.url, downloadUrl);
|
||||
assert.equal(state.results.huggingface.download_url, downloadUrl);
|
||||
assert.equal(state.results.huggingface.model_url, pageUrl);
|
||||
assert.equal(missing.download_source.url, downloadUrl);
|
||||
assert.equal(missing.download_source.model_url, pageUrl);
|
||||
assert.equal(missing.download_source.sha256, sha256);
|
||||
});
|
||||
|
||||
test('batch sources advance independently while each source stays sequential', async () => {
|
||||
const runBatch = eval(`(${extractMethod(resolveDownloadMethodsSource, 'searchMissingBatch')})`);
|
||||
const started = [];
|
||||
|
||||
@@ -45,7 +45,7 @@ test('HuggingFace download rows expose the Show More action', () => {
|
||||
);
|
||||
assert.match(
|
||||
resolveDownloadSource,
|
||||
/if \(hfResult && hfResult\.url\)[\s\S]*?detailsContext:\s*\{[\s\S]*?\.\.\.hfResult,[\s\S]*?details_source:\s*'huggingface'/
|
||||
/const hfDownloadUrl = hfResult\?\.download_url \|\| hfResult\?\.url \|\| ''[\s\S]*?if \(hfResult && hfDownloadUrl\)[\s\S]*?detailsContext:\s*\{[\s\S]*?\.\.\.hfResult,[\s\S]*?details_source:\s*'huggingface'/
|
||||
);
|
||||
});
|
||||
|
||||
@@ -55,6 +55,19 @@ test('HuggingFace details requests keep folder, branch, and token context', () =
|
||||
assert.match(modelInfoSource, /hf_token: tokens\.hf_token \|\| ''/);
|
||||
});
|
||||
|
||||
test('selected HuggingFace details reuse the row and download the direct file URL', () => {
|
||||
assert.match(
|
||||
resolveDownloadSource,
|
||||
/const hfDownloadUrl = hfResult\?\.download_url \|\| hfResult\?\.url \|\| ''/
|
||||
);
|
||||
assert.match(resolveDownloadSource, /downloadUrl: hfDownloadUrl/);
|
||||
assert.match(
|
||||
modelInfoSource,
|
||||
/const isHuggingFace = sourceKey === 'huggingface'[\s\S]*?url: isHuggingFace \? selection\.download_url : selection\.url/
|
||||
);
|
||||
assert.match(modelInfoSource, /model_url: sourceResult\.model_url \|\| sourceResult\.url/);
|
||||
});
|
||||
|
||||
test('a selected HuggingFace variant keeps its repository path', () => {
|
||||
assert.match(
|
||||
modelInfoSource,
|
||||
|
||||
@@ -130,7 +130,8 @@ export const resolveDownloadMethods = {
|
||||
{
|
||||
source: 'huggingface',
|
||||
result: first(results.huggingface),
|
||||
urlKey: 'url',
|
||||
urlKey: 'download_url',
|
||||
fallbackUrlKey: 'url',
|
||||
filenameKey: 'filename'
|
||||
},
|
||||
{
|
||||
@@ -155,7 +156,8 @@ export const resolveDownloadMethods = {
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const result = candidate.result;
|
||||
const url = result?.[candidate.urlKey];
|
||||
const url = result?.[candidate.urlKey]
|
||||
|| (candidate.fallbackUrlKey ? result?.[candidate.fallbackUrlKey] : '');
|
||||
if (!url) continue;
|
||||
const rawModelUrl = result.model_url || result.url || url;
|
||||
|
||||
@@ -3859,7 +3861,8 @@ export const resolveDownloadMethods = {
|
||||
}));
|
||||
}
|
||||
|
||||
if (hfResult && hfResult.url) {
|
||||
const hfDownloadUrl = hfResult?.download_url || hfResult?.url || '';
|
||||
if (hfResult && hfDownloadUrl) {
|
||||
const hfRepo = hfResult.repo_id || hfResult.repo || '';
|
||||
const hfModelUrl = hfRepo ? `https://huggingface.co/${hfRepo}` : getModelCardUrl(hfResult.url);
|
||||
const missingCategory = this.getMissingDownloadCategory?.(missing, 'checkpoints') || missing.category || 'checkpoints';
|
||||
@@ -3870,7 +3873,7 @@ export const resolveDownloadMethods = {
|
||||
model: hfRepo || hfResult.filename,
|
||||
filename: hfResult.filename,
|
||||
secondary: hfResult.path && hfResult.path !== hfResult.filename ? hfResult.path : '',
|
||||
downloadUrl: hfResult.url,
|
||||
downloadUrl: hfDownloadUrl,
|
||||
downloadFilename: hfResult.filename,
|
||||
category: missingCategory,
|
||||
openUrl: hfModelUrl,
|
||||
|
||||
@@ -3549,6 +3549,13 @@ export const modelInfoMethods = {
|
||||
}
|
||||
|
||||
const sourceKey = String(selection.source || contextModel.details_source || contextModel.source || '').toLowerCase();
|
||||
const isHuggingFace = sourceKey === 'huggingface';
|
||||
const sourcePageUrl = selection.page_url
|
||||
|| selection.pageUrl
|
||||
|| selection.version_url
|
||||
|| selection.versionUrl
|
||||
|| selection.url
|
||||
|| '';
|
||||
const selectedBaseModel = selection.base_model || contextModel.base_model || '';
|
||||
const selectionHashes = selection.hashes && typeof selection.hashes === 'object' ? selection.hashes : {};
|
||||
const selectionFile = selection.file_info || selection.selected_file || selection.file || null;
|
||||
@@ -3565,7 +3572,7 @@ export const modelInfoMethods = {
|
||||
version_name: selection.version_name,
|
||||
type: selection.type,
|
||||
filename: selection.filename,
|
||||
url: selection.url,
|
||||
url: isHuggingFace ? selection.download_url : selection.url,
|
||||
download_url: selection.download_url,
|
||||
size: selection.size,
|
||||
base_model: selectedBaseModel,
|
||||
@@ -3578,6 +3585,12 @@ export const modelInfoMethods = {
|
||||
confidence: selection.confidence || 100,
|
||||
searchedAt: new Date().toISOString()
|
||||
};
|
||||
if (isHuggingFace) {
|
||||
sourceResult.repo_id = selection.repo_id || selection.repo || selection.model_id || '';
|
||||
sourceResult.version_url = sourcePageUrl;
|
||||
sourceResult.page_url = sourcePageUrl;
|
||||
sourceResult.model_url = sourcePageUrl;
|
||||
}
|
||||
if (Object.keys(selectionHashes).length) sourceResult.hashes = selectionHashes;
|
||||
if (selectionFile && typeof selectionFile === 'object') {
|
||||
sourceResult.file_info = selectionFile;
|
||||
@@ -3591,7 +3604,7 @@ export const modelInfoMethods = {
|
||||
missing.download_source = {
|
||||
...sourceResult,
|
||||
url: sourceResult.download_url,
|
||||
model_url: sourceResult.url,
|
||||
model_url: sourceResult.model_url || sourceResult.url,
|
||||
directory: missing.category || 'checkpoints'
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user