refactor: unify workflow progress polling

This commit is contained in:
Dariusz L
2026-08-07 17:28:12 +02:00
parent adc409404a
commit 5fa1e76902
2 changed files with 90 additions and 6 deletions
@@ -6,6 +6,7 @@ import { Window } from 'happy-dom';
import {
html,
normalizePathIdentity,
pollBackgroundTask,
safeStorage,
} from '../web/resolver/utils/html_utils.js';
import { getModelCardUrl, getSourceKeyFromUrl } from '../web/resolver/utils/url_utils.js';
@@ -55,6 +56,7 @@ void collectNormalizedCategoryValues;
void CATEGORY_ALIASES;
void normalizeCategoryToken;
void normalizeSha256;
void pollBackgroundTask;
const projectRoot = path.resolve(import.meta.dirname, '..');
const queueMethodsSource = fs.readFileSync(
@@ -2037,6 +2039,7 @@ test('background Loaded Models refresh keeps the current view until new data is
});
test('Loaded Models progress patches the stable progress container', () => {
const pollWorkflowProgress = extractMethod(renderFormatMethodsSource, 'pollWorkflowProgress');
const patchLoadedModelsProgress = extractMethod(
renderFormatMethodsSource,
'patchLoadedModelsProgress'
@@ -2049,17 +2052,81 @@ test('Loaded Models progress patches the stable progress container', () => {
const patchAnalysisProgress = extractMethod(renderFormatMethodsSource, 'patchAnalysisProgress');
const pollAnalysisProgress = extractMethod(renderFormatMethodsSource, 'pollAnalysisProgress');
assert.match(pollWorkflowProgress, /pollBackgroundTask\(/);
assert.match(patchLoadedModelsProgress, /mr-download-section/);
assert.match(patchLoadedModelsProgress, /mr-progress-fill/);
assert.match(patchLoadedModelsProgress, /currentInfo/);
assert.match(pollLoadedModelsProgress, /this\.pollWorkflowProgress\(/);
assert.doesNotMatch(pollLoadedModelsProgress, /pollBackgroundTask\(/);
assert.match(pollLoadedModelsProgress, /patchLoadedModelsProgress\(/);
assert.doesNotMatch(pollLoadedModelsProgress, /contentElement\.innerHTML\s*=/);
assert.match(loadLoadedModels, /patchLoadedModelsProgress\(/);
assert.match(patchAnalysisProgress, /patchLoadedModelsProgress\(/);
assert.match(pollAnalysisProgress, /this\.pollWorkflowProgress\(/);
assert.doesNotMatch(pollAnalysisProgress, /pollBackgroundTask\(/);
assert.match(pollAnalysisProgress, /patchAnalysisProgress\(/);
assert.doesNotMatch(pollAnalysisProgress, /contentElement\.innerHTML\s*=/);
});
test('workflow progress polling preserves endpoint, rendering, and token cleanup', async () => {
const pollWorkflowProgress = eval(`(${extractMethod(renderFormatMethodsSource, 'pollWorkflowProgress')})`);
const pollAnalysisProgress = eval(`(${extractMethod(renderFormatMethodsSource, 'pollAnalysisProgress')})`);
const pollLoadedModelsProgress = eval(`(${extractMethod(renderFormatMethodsSource, 'pollLoadedModelsProgress')})`);
const cases = [
{
method: pollAnalysisProgress,
activeTab: 'missing',
tokenProperty: '_analysisProgressToken',
id: 'analysis-1',
endpoint: '/model_resolver/analyze-progress/analysis-1',
responses: [{ status: 'completed' }],
renderMethod: 'renderAnalysisProgress',
patchMethod: 'patchAnalysisProgress',
},
{
method: pollLoadedModelsProgress,
activeTab: 'loaded',
tokenProperty: '_loadedModelsProgressToken',
id: 'loaded/model',
endpoint: '/model_resolver/loaded-progress/loaded%2Fmodel',
responses: [{ status: 'unknown' }, { status: 'completed' }],
renderMethod: 'renderLoadedModelsProgress',
patchMethod: 'patchLoadedModelsProgress',
},
];
for (const testCase of cases) {
const requests = [];
const rendered = [];
const patched = [];
let responseIndex = 0;
const dialog = {
activeTab: testCase.activeTab,
contentElement: {},
[testCase.tokenProperty]: testCase.id,
pollWorkflowProgress,
fetchJson: async endpoint => {
requests.push(endpoint);
return testCase.responses[responseIndex++];
},
[testCase.renderMethod]: progress => {
rendered.push(progress.status);
return `<div>${progress.status}</div>`;
},
[testCase.patchMethod]: (_container, content) => {
patched.push(content);
},
};
await testCase.method.call(dialog, testCase.id, testCase.id);
assert.deepEqual(requests, Array(testCase.responses.length).fill(testCase.endpoint));
assert.equal(dialog[testCase.tokenProperty], null);
assert.deepEqual(rendered, ['completed']);
assert.deepEqual(patched, ['<div>completed</div>']);
}
});
test('Power Lora Loader strength updates only its Loaded Models chip and cache', () => {
const updateLoadedModelStrengthsFromNode = eval(
`(${extractMethod(tabsLoadedMethodsSource, 'updateLoadedModelStrengthsFromNode')})`
+23 -6
View File
@@ -280,8 +280,29 @@ export const renderFormatMethods = {
this.patchLoadedModelsProgress(container, renderedHtml);
},
async pollAnalysisProgress(analysisId, token) {
async pollWorkflowProgress({
endpoint,
tokenCheck,
onProgress,
onTerminal,
onError,
filterIgnoredStatus,
}) {
await pollBackgroundTask({
endpoint,
tokenCheck,
onProgress,
isTerminal: (progress) => progress.status === 'completed' || progress.status === 'error',
onTerminal,
onError,
intervalMs: 250,
fetchJson: this.fetchJson.bind(this),
filterIgnoredStatus,
});
},
async pollAnalysisProgress(analysisId, token) {
await this.pollWorkflowProgress({
endpoint: `/model_resolver/analyze-progress/${analysisId}`,
tokenCheck: () => this._analysisProgressToken === token,
onProgress: (progress) => {
@@ -301,13 +322,11 @@ export const renderFormatMethods = {
onError: (error) => {
console.warn('Model Resolver: analysis progress polling failed', error);
},
intervalMs: 250,
fetchJson: this.fetchJson.bind(this)
});
},
async pollLoadedModelsProgress(loadedId, token) {
await pollBackgroundTask({
await this.pollWorkflowProgress({
endpoint: `/model_resolver/loaded-progress/${encodeURIComponent(loadedId)}`,
tokenCheck: () => this._loadedModelsProgressToken === token,
filterIgnoredStatus: (progress) => progress?.status === 'unknown',
@@ -328,8 +347,6 @@ export const renderFormatMethods = {
onError: (error) => {
console.warn('Model Resolver: loaded models progress polling failed', error);
},
intervalMs: 250,
fetchJson: this.fetchJson.bind(this)
});
},