Update gpupanel.js
This commit is contained in:
+3
-154
@@ -5,8 +5,6 @@ class MultiGPUExtension {
|
||||
// Button styling constants
|
||||
static BUTTON_STYLES = {
|
||||
base: "width: 100%; padding: 8px; font-size: 12px; color: white; border: none; border-radius: 4px; cursor: pointer; transition: background-color 0.2s;",
|
||||
clearMemory: "background-color: #555;",
|
||||
stagger: "background-color: #224422; margin-top: 8px;",
|
||||
success: "background-color: #3ca03c;",
|
||||
error: "background-color: #c04c4c;"
|
||||
};
|
||||
@@ -14,7 +12,6 @@ class MultiGPUExtension {
|
||||
constructor() {
|
||||
this.config = null;
|
||||
this.originalQueuePrompt = api.queuePrompt.bind(api);
|
||||
this.isStaggeredRun = false;
|
||||
|
||||
// --- FIX [1 of 7]: Constructor Race Condition ---
|
||||
// The interceptor is now set up *after* the config is guaranteed to be loaded.
|
||||
@@ -100,41 +97,6 @@ class MultiGPUExtension {
|
||||
return button;
|
||||
}
|
||||
|
||||
async _handleClearMemory(button) {
|
||||
const originalText = button.textContent;
|
||||
button.textContent = "Clearing...";
|
||||
button.disabled = true;
|
||||
|
||||
try {
|
||||
const urlsToClear = [{ name: 'Master', url: window.location.origin }, ...this.enabledWorkers.map(w => ({ name: w.name, url: `http://localhost:${w.port}` }))];
|
||||
|
||||
const promises = urlsToClear.map(target =>
|
||||
fetch(`${target.url}/multigpu/clear_memory`, { method: 'POST', mode: 'cors' })
|
||||
.then(response => ({ ok: response.ok, name: target.name }))
|
||||
.catch(() => ({ ok: false, name: target.name }))
|
||||
);
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
const failures = results.filter(r => !r.ok);
|
||||
|
||||
if (failures.length === 0) {
|
||||
button.textContent = "Success!";
|
||||
button.style.backgroundColor = MultiGPUExtension.BUTTON_STYLES.success.split(':')[1].trim().replace(';', '');
|
||||
} else {
|
||||
button.textContent = "Error! See Console";
|
||||
button.style.backgroundColor = MultiGPUExtension.BUTTON_STYLES.error.split(':')[1].trim().replace(';', '');
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
button.textContent = originalText;
|
||||
button.style.cssText = MultiGPUExtension.BUTTON_STYLES.base + MultiGPUExtension.BUTTON_STYLES.clearMemory;
|
||||
}, 3000);
|
||||
} finally {
|
||||
// --- FIX [2 of 7]: Button State ---
|
||||
// Ensure the button is re-enabled even if an error occurs.
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
renderSidebarContent(el) {
|
||||
el.innerHTML = '';
|
||||
@@ -178,57 +140,6 @@ class MultiGPUExtension {
|
||||
gpuSection.appendChild(gpuList);
|
||||
container.appendChild(gpuSection);
|
||||
|
||||
const actionsSection = document.createElement("div");
|
||||
actionsSection.style.cssText = "padding-top: 10px; margin-bottom: 15px; border-top: 1px solid #444;";
|
||||
|
||||
const clearMemButton = this._createButton("Clear VRAM", (e) => this._handleClearMemory(e.target), MultiGPUExtension.BUTTON_STYLES.clearMemory);
|
||||
actionsSection.appendChild(clearMemButton);
|
||||
|
||||
const staggerButton = this._createButton("Staggered Start", (e) => {
|
||||
// --- FIX [3 of 7]: Button State & Race Condition ---
|
||||
const button = e.target;
|
||||
button.disabled = true;
|
||||
try {
|
||||
this.isStaggeredRun = true;
|
||||
app.queuePrompt(0, 1);
|
||||
} finally {
|
||||
// Re-enable the button after a short delay to allow the prompt to be processed.
|
||||
setTimeout(() => { button.disabled = false; }, 1000);
|
||||
}
|
||||
}, MultiGPUExtension.BUTTON_STYLES.stagger);
|
||||
staggerButton.id = "stagger-queue-button";
|
||||
staggerButton.title = "Run on Master immediately, then on each Worker with a delay.";
|
||||
actionsSection.appendChild(staggerButton);
|
||||
|
||||
const settingsSection = document.createElement("div");
|
||||
settingsSection.style.cssText = "margin-top: 10px;";
|
||||
|
||||
const delayDiv = document.createElement("div");
|
||||
delayDiv.style.cssText = "display: flex; align-items: center; justify-content: space-between;";
|
||||
const delayLabel = document.createElement("label");
|
||||
delayLabel.textContent = "Stagger Delay (s)";
|
||||
delayLabel.htmlFor = "stagger-delay-input";
|
||||
delayLabel.style.cssText = "font-size: 12px; color: #888;";
|
||||
const delayInput = document.createElement("input");
|
||||
delayInput.id = "stagger-delay-input";
|
||||
delayInput.type = "number";
|
||||
delayInput.min = "0";
|
||||
delayInput.step = "0.1";
|
||||
delayInput.style.cssText = "width: 60px; background-color: #222; border: 1px solid #444; border-radius: 4px; padding: 4px; color: white; font-size: 12px;";
|
||||
delayInput.value = (this.config?.settings?.stagger_delay_ms || 15000) / 1000;
|
||||
delayInput.onchange = async (e) => {
|
||||
const valueInSeconds = parseFloat(e.target.value);
|
||||
if(isNaN(valueInSeconds) || valueInSeconds < 0) return;
|
||||
const valueInMs = Math.round(valueInSeconds * 1000);
|
||||
this.config.settings.stagger_delay_ms = valueInMs;
|
||||
await this._updateSetting('stagger_delay_ms', valueInMs);
|
||||
};
|
||||
delayDiv.appendChild(delayLabel);
|
||||
delayDiv.appendChild(delayInput);
|
||||
settingsSection.appendChild(delayDiv);
|
||||
actionsSection.appendChild(settingsSection);
|
||||
|
||||
container.appendChild(actionsSection);
|
||||
|
||||
const summarySection = document.createElement("div");
|
||||
summarySection.style.cssText = "border-top: 1px solid #444; padding-top: 10px;";
|
||||
@@ -257,19 +168,9 @@ class MultiGPUExtension {
|
||||
|
||||
setupInterceptor() {
|
||||
api.queuePrompt = async (number, prompt) => {
|
||||
// --- FIX [4 of 7]: isStaggeredRun Race Condition ---
|
||||
// The check for the flag is now safer. It's read and reset in a way that
|
||||
// prevents the race condition caused by the original 'finally' block.
|
||||
if (this.isEnabled && this.findNodesByClass(prompt.output, "MultiGPUCollector").length > 0) {
|
||||
const handleStaggered = this.isStaggeredRun;
|
||||
this.isStaggeredRun = false; // Consume the flag immediately
|
||||
|
||||
console.log(`[MultiGPU] Intercepting prompt. Staggered mode: ${handleStaggered}`);
|
||||
if (handleStaggered) {
|
||||
return await this.executeStaggeredMultiGPU(prompt);
|
||||
} else {
|
||||
return await this.executeParallelMultiGPU(prompt);
|
||||
}
|
||||
console.log("[MultiGPU] Intercepting prompt. Executing parallel Multi-GPU.");
|
||||
return await this.executeParallelMultiGPU(prompt);
|
||||
}
|
||||
return this.originalQueuePrompt(number, prompt);
|
||||
};
|
||||
@@ -307,54 +208,6 @@ class MultiGPUExtension {
|
||||
}
|
||||
}
|
||||
|
||||
async executeStaggeredMultiGPU(promptWrapper) {
|
||||
try {
|
||||
console.log("[MultiGPU] Staggered Start activated.");
|
||||
const multi_job_id = "mgpu_" + Date.now();
|
||||
const enabledWorkers = this.enabledWorkers;
|
||||
const batchSize = this._getBatchSizeFromPrompt(promptWrapper);
|
||||
await this._prepareMultiGpuJob(multi_job_id);
|
||||
|
||||
// --- FIX [5 of 7]: Synchronization ---
|
||||
// Calculate a dynamic timeout for the master node.
|
||||
// Baseline of 300s + stagger delay for each worker.
|
||||
const staggerDelay = this.config.settings?.stagger_delay_ms || 15000;
|
||||
const requiredTimeout = ((enabledWorkers.length * staggerDelay) / 1000) + 300;
|
||||
console.log(`[MultiGPU] Master collector timeout set to ${requiredTimeout}s to accommodate staggered workers.`);
|
||||
|
||||
const masterPrompt = this._prepareApiPromptForParticipant(
|
||||
promptWrapper.output, multi_job_id, 'master',
|
||||
{
|
||||
enabled_worker_ids: enabledWorkers.map(w => w.id),
|
||||
worker_batch_size: batchSize,
|
||||
stagger_timeout: requiredTimeout // Pass the new timeout to the master
|
||||
}
|
||||
);
|
||||
this.originalQueuePrompt(0, { ...promptWrapper, output: masterPrompt });
|
||||
console.log("[MultiGPU] Dispatched to Master immediately.");
|
||||
|
||||
console.log(`[MultiGPU] Beginning staggered dispatch to workers with a ${staggerDelay / 1000}s delay.`);
|
||||
|
||||
for (const worker of enabledWorkers) {
|
||||
await new Promise(resolve => setTimeout(resolve, staggerDelay));
|
||||
// --- FIX [6 of 7]: Missing Parameters ---
|
||||
// The options object was missing here, which prevented the 'master_url'
|
||||
// from being set correctly for the worker.
|
||||
const workerPrompt = this._prepareApiPromptForParticipant(
|
||||
promptWrapper.output,
|
||||
multi_job_id,
|
||||
worker.id,
|
||||
{ enabled_worker_ids: [], worker_batch_size: 0 } // This ensures master_url is set
|
||||
);
|
||||
await this._dispatchToWorker(worker, workerPrompt, promptWrapper.workflow);
|
||||
}
|
||||
return { "prompt_id": "multi-gpu-staggered-job-dispatched" };
|
||||
} catch (error) {
|
||||
console.error("[MultiGPU] Staggered execution failed:", error);
|
||||
alert(`[MultiGPU] Staggered execution failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async _executeJobs(jobs) {
|
||||
const promises = jobs.map(job =>
|
||||
@@ -399,10 +252,6 @@ class MultiGPUExtension {
|
||||
if (isMaster) {
|
||||
inputs.enabled_worker_ids = JSON.stringify(options.enabled_worker_ids || []);
|
||||
inputs.worker_batch_size = options.worker_batch_size || 1;
|
||||
// Pass the dynamic timeout for staggered mode
|
||||
if (options.stagger_timeout) {
|
||||
inputs.stagger_timeout = options.stagger_timeout;
|
||||
}
|
||||
} else {
|
||||
inputs.master_url = window.location.origin;
|
||||
}
|
||||
@@ -449,4 +298,4 @@ app.registerExtension({
|
||||
async setup() {
|
||||
new MultiGPUExtension();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user