diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index fee905c..b85307a 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -2349,6 +2349,73 @@ test('cached Missing Models refresh preserves browser geometry after linking a l ]); }); +test('workflow analysis and loaded model caches stay independent and cloned', () => { + const saveWorkflowModelCache = eval( + `(${extractMethod(workflowStateMethodsSource, 'saveWorkflowModelCache')})` + ); + const restoreWorkflowModelCache = eval( + `(${extractMethod(workflowStateMethodsSource, 'restoreWorkflowModelCache')})` + ); + const saveAnalysisCacheForActiveWorkflow = eval( + `(${extractMethod(workflowStateMethodsSource, 'saveAnalysisCacheForActiveWorkflow')})` + ); + const restoreAnalysisCacheForActiveWorkflow = eval( + `(${extractMethod(workflowStateMethodsSource, 'restoreAnalysisCacheForActiveWorkflow')})` + ); + const saveLoadedModelsCacheForActiveWorkflow = eval( + `(${extractMethod(workflowStateMethodsSource, 'saveLoadedModelsCacheForActiveWorkflow')})` + ); + const restoreLoadedModelsCacheForActiveWorkflow = eval( + `(${extractMethod(workflowStateMethodsSource, 'restoreLoadedModelsCacheForActiveWorkflow')})` + ); + const dialog = { + workflowKey: 'workflow-a\nsignature-a', + workflowAnalysisCaches: new Map(), + workflowLoadedModelCaches: new Map(), + cachedWorkflowSignature: 'analysis-signature', + cachedAnalysisData: { missing_models: [{ filename: 'missing.safetensors' }] }, + cachedLoadedModelsSignature: 'loaded-signature', + cachedLoadedModelsData: { loaded_models: [{ filename: 'loaded.safetensors' }] }, + getWorkflowScopedQueueKey() { + return this.workflowKey; + }, + saveWorkflowModelCache, + restoreWorkflowModelCache, + cloneAnalysisData(data) { + return JSON.parse(JSON.stringify(data)); + }, + }; + + saveAnalysisCacheForActiveWorkflow.call(dialog); + saveLoadedModelsCacheForActiveWorkflow.call(dialog); + + dialog.cachedAnalysisData.missing_models[0].filename = 'changed-analysis.safetensors'; + dialog.cachedLoadedModelsData.loaded_models[0].filename = 'changed-loaded.safetensors'; + assert.equal( + dialog.workflowAnalysisCaches.get(dialog.workflowKey).data.missing_models[0].filename, + 'missing.safetensors' + ); + assert.equal( + dialog.workflowLoadedModelCaches.get(dialog.workflowKey).data.loaded_models[0].filename, + 'loaded.safetensors' + ); + + dialog.cachedWorkflowSignature = null; + dialog.cachedAnalysisData = null; + dialog.cachedLoadedModelsSignature = null; + dialog.cachedLoadedModelsData = null; + restoreAnalysisCacheForActiveWorkflow.call(dialog); + restoreLoadedModelsCacheForActiveWorkflow.call(dialog); + + assert.equal(dialog.cachedWorkflowSignature, 'analysis-signature'); + assert.equal(dialog.cachedLoadedModelsSignature, 'loaded-signature'); + assert.equal(dialog.cachedAnalysisData.missing_models[0].filename, 'missing.safetensors'); + assert.equal(dialog.cachedLoadedModelsData.loaded_models[0].filename, 'loaded.safetensors'); + + dialog.cachedAnalysisData.missing_models.push({ filename: 'local-only.safetensors' }); + assert.equal(dialog.workflowAnalysisCaches.get(dialog.workflowKey).data.missing_models.length, 1); +}); + test('node widget changes request a content-preserving Missing Models refresh', async () => { const log = { debug() {} }; const refreshForActiveWorkflowChange = eval( diff --git a/web/resolver/shell/workflow_state_methods.js b/web/resolver/shell/workflow_state_methods.js index ac0ccd4..b1ba0aa 100644 --- a/web/resolver/shell/workflow_state_methods.js +++ b/web/resolver/shell/workflow_state_methods.js @@ -463,50 +463,55 @@ export const workflowStateMethods = { } }, - saveAnalysisCacheForActiveWorkflow() { + saveWorkflowModelCache(cache, signature, data) { const key = this.getWorkflowScopedQueueKey(); if (!key) return; - if (this.cachedWorkflowSignature && this.cachedAnalysisData) { - this.workflowAnalysisCaches.set(key, { - signature: this.cachedWorkflowSignature, - data: this.cloneAnalysisData(this.cachedAnalysisData) + if (signature && data) { + cache.set(key, { + signature, + data: this.cloneAnalysisData(data) }); } else { - this.workflowAnalysisCaches.delete(key); + cache.delete(key); } }, + restoreWorkflowModelCache(cache) { + const key = this.getWorkflowScopedQueueKey(); + const saved = key ? cache.get(key) : null; + return { + signature: saved?.signature || null, + data: saved?.data ? this.cloneAnalysisData(saved.data) : null + }; + }, + + saveAnalysisCacheForActiveWorkflow() { + this.saveWorkflowModelCache( + this.workflowAnalysisCaches, + this.cachedWorkflowSignature, + this.cachedAnalysisData + ); + }, + restoreAnalysisCacheForActiveWorkflow() { - const key = this.getWorkflowScopedQueueKey(); - const saved = key ? this.workflowAnalysisCaches.get(key) : null; - this.cachedWorkflowSignature = saved?.signature || null; - this.cachedAnalysisData = saved?.data - ? this.cloneAnalysisData(saved.data) - : null; + const saved = this.restoreWorkflowModelCache(this.workflowAnalysisCaches); + this.cachedWorkflowSignature = saved.signature; + this.cachedAnalysisData = saved.data; }, saveLoadedModelsCacheForActiveWorkflow() { - const key = this.getWorkflowScopedQueueKey(); - if (!key) return; - - if (this.cachedLoadedModelsSignature && this.cachedLoadedModelsData) { - this.workflowLoadedModelCaches.set(key, { - signature: this.cachedLoadedModelsSignature, - data: this.cloneAnalysisData(this.cachedLoadedModelsData) - }); - } else { - this.workflowLoadedModelCaches.delete(key); - } + this.saveWorkflowModelCache( + this.workflowLoadedModelCaches, + this.cachedLoadedModelsSignature, + this.cachedLoadedModelsData + ); }, restoreLoadedModelsCacheForActiveWorkflow() { - const key = this.getWorkflowScopedQueueKey(); - const saved = key ? this.workflowLoadedModelCaches.get(key) : null; - this.cachedLoadedModelsSignature = saved?.signature || null; - this.cachedLoadedModelsData = saved?.data - ? this.cloneAnalysisData(saved.data) - : null; + const saved = this.restoreWorkflowModelCache(this.workflowLoadedModelCaches); + this.cachedLoadedModelsSignature = saved.signature; + this.cachedLoadedModelsData = saved.data; }, invalidateLoadedModelsCacheForActiveWorkflow() {