add timer info

Signed-off-by: vladmandic <mandic00@live.com>
This commit is contained in:
vladmandic
2026-04-23 12:49:41 +02:00
parent 21b34a6fa7
commit 40e550f1c3
23 changed files with 194 additions and 76 deletions
+3 -1
View File
@@ -115,12 +115,13 @@ For full details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/m
- refactor `hash-cache` management, thanks @awsr
- validate all `reference` jsons and backfill all fields
- sticter `js` linting, thanks @awsr
- ui: add profiling info
- ui: remove non-passive event listeners
- ui: add debounce to ui updates
- ui: utilize requestanimationframe for paint optimizations
- ui: profile callbacks
- ui: validate callbacks before use, thanks @awsr
- ui: log formatting
- ui: log formatting
- **Fixes**
- Prohibit `python==3.14` unless `--experimental`
- UI CSS fixes, thanks @awsr
@@ -139,6 +140,7 @@ For full details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/m
- controlnet processor error handling
- error handling for same-device check
- error handling for undefined pipeline
- erorr handling for `scripts` loader
- patch `z-image` for fp16 compatibility, thanks @resonantsky
- patch `unipc` for timesteps device placement, thanks @resonantsky
- `civitai` search and base-model discovery improvements
+2
View File
@@ -35,6 +35,7 @@ const jsConfig = defineConfig([
log: 'readonly',
debug: 'readonly',
error: 'readonly',
timer: 'readonly',
xhrGet: 'readonly',
xhrPost: 'readonly',
gradioApp: 'readonly',
@@ -52,6 +53,7 @@ const jsConfig = defineConfig([
getUICurrentTabContent: 'readonly',
waitForFlag: 'readonly',
logFn: 'readonly',
logTimers: 'readonly',
generateForever: 'readonly',
showContributors: 'readonly',
opts: 'writable',
+4 -2
View File
@@ -48,7 +48,7 @@ function dimensionChange(e, is_width, is_height) {
}
}
onAfterUiUpdate(() => {
function aspectRatioCallback() {
const arPreviewRect = gradioApp().querySelector('#imageARPreview');
if (arPreviewRect) arPreviewRect.style.display = 'none';
const tabImg2img = gradioApp().querySelector('#tab_img2img');
@@ -68,4 +68,6 @@ onAfterUiUpdate(() => {
});
}
}
});
}
onAfterUiUpdate(aspectRatioCallback);
+13 -5
View File
@@ -148,6 +148,7 @@ const engine = {
this.indices.clear();
return;
}
const t0 = performance.now();
const toLoad = enabled.filter((n) => !this.indices.has(n));
const toRemove = [...this.indices.keys()].filter((n) => !enabled.includes(n));
toRemove.forEach((n) => this.indices.delete(n));
@@ -164,7 +165,9 @@ const engine = {
if (cat.name) this.categoryNames[id] = cat.name;
});
}
log('autoComplete', { loaded: name, tags: data.tags?.length || 0 });
const t1 = performance.now();
log('autoComplete', { loaded: name, tags: data.tags?.length || 0, time: Math.round(t1 - t0) });
timer(`autocompleteLoad:${name}`, t1 - t0);
} catch (e) {
log('autoComplete', { failed: name, error: e });
}
@@ -508,6 +511,7 @@ function patchConfigBridge() {
// -- Initialization --
async function initAutocomplete() {
const t0 = performance.now();
const enabled = window.opts?.autocomplete_enabled || [];
active = window.opts?.autocomplete_active || false;
log('autoComplete', { active, enabled });
@@ -543,9 +547,9 @@ async function initAutocomplete() {
attached++;
}
});
log('autoComplete', { attached, dicts: engine.indices.size });
// Reload when settings change
onOptionsChanged(async () => {
async function optionsChangedCallback() {
const newActive = window.opts?.autocomplete_active || false;
const newEnabled = window.opts?.autocomplete_enabled || [];
const currentKeys = [...engine.indices.keys()].sort().join(',');
@@ -556,9 +560,13 @@ async function initAutocomplete() {
active = newActive;
patchActiveButton();
}
});
}
onOptionsChanged(optionsChangedCallback);
// Watch for config updates from the script UI bridge
patchConfigBridge();
patchActiveButton();
onAfterUiUpdate(() => patchConfigBridge());
onAfterUiUpdate(patchConfigBridge);
const t1 = performance.now();
log('autoComplete', { attached, dicts: engine.indices.size, time: Math.round(t1 - t0) });
timer('autocompleteInit', t1 - t0);
}
+4 -1
View File
@@ -17,6 +17,7 @@ function controlInputMode(inputMode, ...args) {
}
async function setupControlUI() {
const t0 = performance.now();
const tabs = ['input', 'output', 'preview'];
for (const tab of tabs) {
const btn = gradioApp().getElementById(`control-${tab}-button`);
@@ -46,5 +47,7 @@ async function setupControlUI() {
});
intersectionObserver.observe(el); // monitor visibility of tab
log('initControlUI');
const t1 = performance.now();
log('setupControlUI', Math.round(t1 - t0));
timer('setupControlUI', t1 - t0);
}
+2
View File
@@ -217,6 +217,7 @@ async function filterExtraNetworksForTab(searchTerm) {
}
const t1 = performance.now();
log(`filterExtraNetworks: text="${searchTerm}" items=${items} match=${found} time=${Math.round(t1 - t0)}`);
timer(`filterExtraNetworks:${searchTerm}`, t1 - t0);
}
function tryToRemoveExtraNetworkFromPrompt(textarea, text) {
@@ -282,6 +283,7 @@ function sortExtraNetworks(fixed = 'no') {
const desc = sortDesc[sortVal];
const t1 = performance.now();
log('sortNetworks', { name: pagename, val: sortVal, order: desc, fixed: fixed === 'fixed', items: num, time: Math.round(t1 - t0) });
timer(`sortExtraNetworks:${desc}`, t1 - t0);
return desc;
}
+11 -4
View File
@@ -855,7 +855,8 @@ async function gallerySearch() {
}
const t1 = performance.now();
updateStatusWithSort('Filter', ['Images', `${totalFound.toLocaleString()} / ${allFiles.length.toLocaleString()}`], `${iconStopwatch} ${Math.floor(t1 - t0).toLocaleString()}ms`);
updateStatusWithSort('Filter', ['Images', `${totalFound.toLocaleString()} / ${allFiles.length.toLocaleString()}`], `${iconStopwatch} ${Math.round(t1 - t0).toLocaleString()}ms`);
timer(`galleryFilter:${str}`, t1 - t0);
refreshGallerySelection();
}, 250);
}
@@ -936,7 +937,8 @@ async function gallerySort(key) {
const t1 = performance.now();
log(`gallerySort: sort=${sortMode.name} len=${arr.length} time=${Math.floor(t1 - t0)}`);
updateStatusWithSort(['Images', arr.length.toLocaleString()], `${iconStopwatch} ${Math.floor(t1 - t0).toLocaleString()}ms`);
updateStatusWithSort(['Images', arr.length.toLocaleString()], `${iconStopwatch} ${Math.round(t1 - t0).toLocaleString()}ms`);
timer(`gallerySort:${sortMode.name}`, t1 - t0);
refreshGallerySelection();
}
@@ -1025,7 +1027,8 @@ async function thumbCacheCleanup(folder, imgCount, controller, force = false) {
await idbFolderCleanup(keptGalleryHashes, recursiveFolder, controller.signal)
.then((delcount) => {
const t1 = performance.now();
log(`Thumbnail DB cleanup: folder=${folder} kept=${keptGalleryHashes.size} deleted=${delcount} time=${Math.floor(t1 - t0)}ms`);
log(`Thumbnail DB cleanup: folder=${folder} kept=${keptGalleryHashes.size} deleted=${delcount} time=${Math.round(t1 - t0)}ms`);
timer(`thumbnailDBCleanup:${folder}`, t1 - t0);
currentGalleryFolder = null;
el.clearCacheFolder.innerText = '<select a folder first>';
updateStatusWithSort('Thumbnail cache cleared');
@@ -1139,6 +1142,7 @@ async function fetchFilesHT(evt, controller) {
const t1 = performance.now();
log(`gallery: folder=${evt.target.name} num=${numFiles} time=${Math.floor(t1 - t0)}ms`);
timer(`galleryFetch:${evt.target.name}`, t1 - t0);
updateStatusWithSort(['Folder', evt.target.name], ['Images', numFiles.toLocaleString()], `${iconStopwatch} ${Math.floor(t1 - t0).toLocaleString()}ms`);
galleryProgressBar.start(numFiles);
addSeparators();
@@ -1320,7 +1324,7 @@ async function blockQueueUntilReady() {
}
async function initGallery() { // triggered on gradio change to monitor when ui gets sufficiently constructed
log('initGallery');
const t0 = performance.now();
el.folders = gradioApp().getElementById('tab-gallery-folders');
el.files = gradioApp().getElementById('tab-gallery-files');
el.status = gradioApp().getElementById('tab-gallery-status');
@@ -1363,6 +1367,9 @@ async function initGallery() { // triggered on gradio change to monitor when ui
'outdir_img2img_grids',
'outdir_control_grids',
].forEach((op) => { monitorOption(op, updateFolders); });
const t1 = performance.now();
log('initGallery', Math.round(t1 - t0));
timer('initGallery', t1 - t0);
}
// register on startup
+4 -1
View File
@@ -20,6 +20,7 @@ let control_gallery;
let modal;
async function initiGenerationParams() {
const t0 = performance.now();
if (!modal) modal = gradioApp().getElementById('lightboxModal');
if (!modal) return;
@@ -37,5 +38,7 @@ async function initiGenerationParams() {
if (!img2img_gallery) img2img_gallery = attachGalleryListeners('img2img');
if (!control_gallery) control_gallery = attachGalleryListeners('control');
modalObserver.observe(modal, { attributes: true, attributeFilter: ['style'] });
log('initGenerationParams');
const t1 = performance.now();
log('initGenerationParams', Math.round(t1 - t0));
timer('initGenerationParams', t1 - t0);
}
+4 -1
View File
@@ -235,6 +235,7 @@ async function bindImageViewer() {
}
async function initImageViewer() {
const t0 = performance.now();
// main elements
const modal = document.createElement('div');
modal.id = 'lightboxModal';
@@ -341,7 +342,9 @@ async function initImageViewer() {
modal.append(modalExif);
gradioApp().appendChild(modal);
log('initImageViewer');
const t1 = performance.now();
log('initImageViewer', Math.round(t1 - t0));
timer('initImageViewer', t1 - t0);
}
onAfterUiUpdate(bindImageViewer);
+1
View File
@@ -66,6 +66,7 @@ async function removeSplash() {
log('removeSplash');
const t = Math.round(performance.now() - appStartTime);
log('startupTime', t);
timer('splashVisible', t);
xhrPost(`${window.api}/log`, { message: `ready time=${t}` });
monitorLogActive = false;
}
+4 -1
View File
@@ -103,6 +103,7 @@ async function logMonitor() {
async function initLogMonitor() {
const el = document.getElementsByTagName('footer')[0];
if (!el) return;
const t0 = performance.now();
el.classList.add('log-monitor');
const ui_disabled = Array.isArray(window.opts.ui_disabled) ? window.opts.ui_disabled : [];
if (ui_disabled.includes('logs')) return;
@@ -126,5 +127,7 @@ async function initLogMonitor() {
el.style.display = 'none';
authFetch(`${window.api}/start?agent=${encodeURI(navigator.userAgent)}`);
logMonitor();
log('initLogMonitor');
const t1 = performance.now();
log('initLogMonitor', Math.round(t1 - t0));
timer('initLogMonitor', t1 - t0);
}
+4 -1
View File
@@ -27,7 +27,7 @@ function setupBracketChecking(idPrompt, idCounter) {
}
async function initPromptChecker() {
log('initPromptChecker');
const t0 = performance.now();
setupBracketChecking('txt2img_prompt', 'txt2img_token_counter');
setupBracketChecking('txt2img_neg_prompt', 'txt2img_negative_token_counter');
setupBracketChecking('img2img_prompt', 'img2img_token_counter');
@@ -36,4 +36,7 @@ async function initPromptChecker() {
setupBracketChecking('control_neg_prompt', 'control_negative_token_counter');
setupBracketChecking('video_prompt', 'video_token_counter');
setupBracketChecking('video_neg_prompt', 'video_negative_token_counter');
const t1 = performance.now();
log('initPromptChecker', Math.round(t1 - t0));
timer('initPromptChecker', t1 - t0);
}
+6 -2
View File
@@ -1,3 +1,5 @@
window.gradioObserver = null;
async function sleep(ms) {
return new Promise((resolve) => { setTimeout(resolve, ms); });
}
@@ -15,6 +17,7 @@ function logFn(func) { // not recommended: use log, debug or error explicitly
const returnValue = func(...arguments);
const t1 = performance.now();
log(func.name, `time=${Math.round(t1 - t0)}`);
timer(func.name, t1 - t0);
return returnValue;
};
}
@@ -95,6 +98,7 @@ function executeCallbacks(queue, arg) {
callback(arg);
const t1 = performance.now();
if (t1 - t0 > 250) log('callbackSlow', callback.name || callback, `time=${Math.round(t1 - t0)}`);
timer(callback.name || 'anonymousCallback', t1 - t0);
} catch (e) {
error(`executeCallbacks: ${callback} ${e}`);
}
@@ -149,8 +153,8 @@ async function mutationCallback(mutations) {
document.addEventListener('DOMContentLoaded', () => {
log('DOMContentLoaded');
const mutationObserver = new MutationObserver(mutationCallback);
mutationObserver.observe(gradioApp(), { childList: true, subtree: true, attributes: false });
window.gradioObserver = new MutationObserver(mutationCallback);
window.gradioObserver.observe(gradioApp(), { childList: true, subtree: true, attributes: false });
});
/**
+7 -6
View File
@@ -1,3 +1,4 @@
window.hintsObserver = null;
const allLocales = ['en', 'tb', 'nb', 'hr', 'es', 'it', 'fr', 'de', 'pt', 'ru', 'zh', 'ja', 'ko', 'hi', 'ar', 'bn', 'ur', 'id', 'vi', 'tr', 'sr', 'po', 'he', 'xx', 'qq', 'tlh'];
const localeData = {
prev: null,
@@ -11,7 +12,6 @@ const localeData = {
btn: null,
expandTimeout: null, // Property for expansion timeout
currentElement: null, // Track current element for expansion
observer: null, // MutationObserver for DOM changes
};
let localeTimeout = null;
const isTouchDevice = 'ontouchstart' in window;
@@ -73,7 +73,7 @@ async function tooltipCreate() {
gradioApp().addEventListener('pointerover', tooltipShowDelegated); // eslint-disable-line no-use-before-define
gradioApp().addEventListener('pointerout', tooltipHideDelegated); // eslint-disable-line no-use-before-define
}
if (!localeData.observer) initializeDOMObserver(); // eslint-disable-line no-use-before-define
if (!window.hintsObserver) initializeDOMObserver(); // eslint-disable-line no-use-before-define
}
async function expandTooltip(element, longHint) {
@@ -345,6 +345,7 @@ async function setHints() {
localeData.finished = true;
localeData.initial = false;
const t1 = performance.now();
timer('setHints', t1 - t0);
// localeData.btn.style.backgroundColor = localeData.locale !== 'en' ? 'var(--primary-500)' : '';
log('touchDevice', isTouchDevice);
log('setHints', { type: localeData.type, locale: localeData.locale, elements: elements.length, localized, hints, data: localeData.data.length, override: overrideData.length, time: Math.round(t1 - t0) });
@@ -379,11 +380,11 @@ async function applyHintToElement(el) {
// Initialize MutationObserver for immediate hint application
function initializeDOMObserver() {
if (localeData.observer) {
localeData.observer.disconnect();
if (window.hintsObserver) {
window.hintsObserver.disconnect();
}
localeData.observer = new MutationObserver((mutations) => {
window.hintsObserver = new MutationObserver((mutations) => {
// Process added nodes immediately
for (const mutation of mutations) {
if (mutation.type === 'childList') {
@@ -421,7 +422,7 @@ function initializeDOMObserver() {
// Start observing the entire gradio app for changes
const targetNode = gradioApp();
if (targetNode) {
localeData.observer.observe(targetNode, {
window.hintsObserver.observe(targetNode, {
childList: true,
subtree: true,
});
+7 -3
View File
@@ -46,9 +46,7 @@ async function updateOpts(json_string) {
}
}
const t2 = performance.now();
window.opts = new_opts;
log('updateOpts', `settings=${Object.keys(new_opts).length} callbacks=${Math.round(t2 - t1)} apply=${Math.round(t1 - t0)}`);
Object.entries(opts_metadata).forEach(([opt, meta]) => {
if (!opts_tabs[meta.tab_name]) opts_tabs[meta.tab_name] = {};
if (!opts_tabs[meta.tab_name].unsaved_keys) opts_tabs[meta.tab_name].unsaved_keys = new Set();
@@ -56,6 +54,9 @@ async function updateOpts(json_string) {
if (!meta.is_stored) opts_tabs[meta.tab_name].unsaved_keys.add(opt);
else opts_tabs[meta.tab_name].saved_keys.add(opt);
});
const t2 = performance.now();
log('updateOpts', `settings=${Object.keys(new_opts).length} callbacks=${Math.round(t2 - t1)} apply=${Math.round(t1 - t0)}`);
timer('updateOpts', t2 - t0);
}
function showAllSettings() {
@@ -201,6 +202,7 @@ async function initModels() {
async function initSettings() {
if (settingsInitialized) return;
const t0 = performance.now();
settingsInitialized = true;
const tabNavElements = gradioApp().querySelector('#settings > .tab-nav');
if (!tabNavElements) {
@@ -228,5 +230,7 @@ async function initSettings() {
tabContentWrapper.appendChild(elem);
observer.observe(elem, { attributes: true, attributeFilter: ['style'] });
});
log('initSettings');
const t1 = performance.now();
log('initSettings', Math.round(t1 - t0));
timer('initSettings', t1 - t0);
}
+42 -23
View File
@@ -2,6 +2,8 @@
window.api = '/sdapi/v1';
window.subpath = '';
const startupPromises = [];
async function waitForOpts() {
// make sure all of the ui is ready and options are loaded
const t0 = performance.now();
@@ -14,7 +16,8 @@ async function waitForOpts() {
if (window.opts && Object.keys(window.opts).length > 0) {
ok = window.opts.theme_type === 'Modern' ? 'uiux_separator_appearance' in window.opts : true;
if (ok) {
log('waitForOpts', `time=${Math.round(t1 - t0)}`);
log('waitForOpts', Math.round(t1 - t0));
timer('waitForOpts', t1 - t0);
break;
}
}
@@ -23,54 +26,70 @@ async function waitForOpts() {
}
}
async function postStartup() {
log('postStartup');
if (window.gradioObserver) window.gradioObserver.disconnect();
if (window.hintsObserver) window.hintsObserver.disconnect();
logTimers();
}
async function initStartup() {
const t0 = performance.now();
log('initGradio', `time=${Math.round(t0 - appStartTime)}`);
log('initGradio', Math.round(t0 - appStartTime));
timer('initGradio', t0 - appStartTime);
log('initUi');
if (window.setupLogger) await setupLogger();
// all items here are non-blocking async calls
initModels();
getUIDefaults();
initPromptChecker();
initContextMenu();
initDragDrop();
initAccordions();
initSettings();
initImageViewer();
initiGenerationParams();
initChangelog();
setupControlUI();
startupPromises.push(initModels());
startupPromises.push(getUIDefaults());
startupPromises.push(initPromptChecker());
startupPromises.push(initContextMenu());
startupPromises.push(initDragDrop());
startupPromises.push(initAccordions());
startupPromises.push(initSettings());
startupPromises.push(initImageViewer());
startupPromises.push(initiGenerationParams());
startupPromises.push(initChangelog());
startupPromises.push(setupControlUI());
// reconnect server session
await reconnectUI();
await waitForOpts();
await initGallery();
log('mountURL', window.opts.subpath);
if (window.opts.subpath?.length > 0) {
window.subpath = window.opts.subpath;
window.api = `${window.subpath}/sdapi/v1`;
}
setRefreshInterval();
executeCallbacks(uiReadyCallbacks);
setupExtraNetworks();
startupPromises.push(initGallery());
startupPromises.push(setRefreshInterval());
startupPromises.push(setupExtraNetworks());
// optinally wait for modern ui
if (window.waitForUiReady) await waitForUiReady();
initAutocomplete();
monitorConnection();
// post startup tasks that may take longer but are not critical
showNetworks();
setHints();
applyStyles();
initIndexDB();
initLogMonitor();
startupPromises.push(initAutocomplete());
startupPromises.push(monitorConnection());
startupPromises.push(showNetworks());
startupPromises.push(setHints());
startupPromises.push(applyStyles());
startupPromises.push(initIndexDB());
startupPromises.push(initLogMonitor());
t1 = performance.now();
log('initStartup', Math.round(1000 * (t1 - t0) / 1000000));
removeSplash();
await Promise.all(startupPromises);
t2 = performance.now();
log('initComplete', Math.round(1000 * (t2 - t0) / 1000000));
postStartup();
}
onUiLoaded(initStartup);
+14
View File
@@ -0,0 +1,14 @@
const allTimers = [];
async function timer(name, elapsed) {
allTimers.push([name, Math.round(elapsed)]);
}
async function logTimers() {
allTimers.sort((a, b) => b[1] - a[1]);
const filteredTimers = allTimers.filter((t) => t[1] > 50);
log('timers', filteredTimers);
// xhrPost(`${window.api}/log`, { debug: JSON.stringify(filteredTimers) });
}
window.timer = timer;
+9 -2
View File
@@ -163,6 +163,7 @@ function setFontSize(val, old) {
appliedFontSize = nextSize;
const t1 = performance.now();
log('setFontSize', nextSize, `time=${Math.round(t1 - t0)}`);
timer('setFontSize', t1 - t0);
});
}
@@ -473,6 +474,7 @@ function registerTextareaCallback() {
// sortUIElements();
if (promptsInitialized) return;
if (promptRegistrationInProgress) return;
const t0 = performance.now();
const app = gradioApp();
if (!app) return;
@@ -540,7 +542,9 @@ function registerTextareaCallback() {
promptsInitialized = registeredPromptIds.size === total;
if (promptsInitialized) {
promptRegistrationInProgress = false;
log('initPrompts', registeredPromptIds.size);
const t1 = performance.now();
log('initPrompts', { count: registeredPromptIds.size, time: Math.round(t1 - t0) });
timer('initPrompts', t1 - t0);
return;
}
@@ -709,6 +713,7 @@ async function browseFolder() {
}
async function reconnectUI() {
const t0 = performance.now();
const gallery = gradioApp().getElementById('txt2img_gallery');
const task_id = localStorage.getItem('task');
const api_logo = Array.from(gradioApp().querySelectorAll('img')).filter((el) => el?.src?.endsWith('api-logo.svg'));
@@ -738,5 +743,7 @@ async function reconnectUI() {
};
const sd_model_observer = new MutationObserver(sd_model_callback);
sd_model_observer.observe(sd_model, { attributes: true, childList: true, subtree: true });
log('reconnectUI');
const t1 = performance.now();
log('reconnectUI', Math.round(t1 - t0));
timer('reconnectUI', t1 - t0);
}
+1
View File
@@ -400,6 +400,7 @@ class ReqGetLog(BaseModel):
class ReqPostLog(BaseModel):
json: dict | None = Field(default=None, title="Data", description="The data to log")
message: str | None = Field(default=None, title="Message", description="The info message to log")
debug: str | None = Field(default=None, title="Debug message", description="The debug message to log")
error: str | None = Field(default=None, title="Error message", description="The error message to log")
+5 -3
View File
@@ -71,11 +71,13 @@ def get_log(req: models.ReqGetLog = Depends()):
return lines
def post_log(req: models.ReqPostLog):
if req.message is not None:
if req.json is not None:
log.info(f'UI {req.message or ""}: {req.json}')
elif req.message is not None:
log.info(f'UI: {req.message}')
if req.debug is not None:
elif req.debug is not None:
log.debug(f'UI: {req.debug}')
if req.error is not None:
elif req.error is not None:
log.error(f'UI: {req.error}')
return {}
+42 -17
View File
@@ -187,20 +187,19 @@ class Script:
"""
pass # pylint: disable=unnecessary-pass
"""
# script can define two methods below, they are not defined by default to skip unnecessary calls for scripts that don't need this functionality
def before_component(self, component: IOComponent, **kwargs):
"""
Called before a component is created.
Use elem_id/label fields of kwargs to figure out which component it is.
This can be useful to inject your own components somewhere in the middle of vanilla UI.
You can return created components in the ui() function to add them to the list of arguments for your processing functions
"""
# Called before a component is created.
# Use elem_id/label fields of kwargs to figure out which component it is.
# This can be useful to inject your own components somewhere in the middle of vanilla UI.
# You can return created components in the ui() function to add them to the list of arguments for your processing functions
pass # pylint: disable=unnecessary-pass
def after_component(self, component: IOComponent, **kwargs):
"""
Called after a component is created. Same as above.
"""
# Called after a component is created. Same as above.
pass # pylint: disable=unnecessary-pass
"""
def describe(self):
"""unused"""
@@ -453,7 +452,6 @@ class ScriptRunner:
script.name = wrap_call(script.title, script.filename, "title", default=script.filename).lower()
api_args = []
for control in controls:
debug(f'Script control: parent={script.parent} script="{script.name}" label="{control.label}" type={control} id={control.elem_id}')
if hasattr(gr.components, 'IOComponent'):
if not isinstance(control, gr.components.IOComponent):
log.error(f'Invalid script control: "{script.filename}" control={control}')
@@ -462,6 +460,7 @@ class ScriptRunner:
if not isinstance(control, gr.components.Component):
log.error(f'Invalid script control: "{script.filename}" control={control}')
continue
debug(f'Script control: parent={script.parent} script="{script.name}" label="{control.label}" type={control} id={control.elem_id}')
control.custom_script_source = os.path.basename(script.filename)
arg_info = api_models.ScriptArg(label=control.label or "")
for field in ("value", "minimum", "maximum", "step", "choices"):
@@ -498,7 +497,10 @@ class ScriptRunner:
continue
t0 = time.time()
with gr.Group(elem_id=f'{parent}_script_{script.title().lower().replace(" ", "_")}', elem_classes=['group-extension']) as group:
create_script_ui(script, inputs, inputs_alwayson)
try:
create_script_ui(script, inputs, inputs_alwayson)
except Exception as e:
errors.display(e, f'Create Script UI: type=internal fn="{script.filename}"')
script.group = group
time_setup[script.title()] = time_setup.get(script.title(), 0) + (time.time()-t0)
@@ -513,7 +515,10 @@ class ScriptRunner:
continue
t0 = time.time()
with gr.Group(elem_id=f'{parent}_script_{script.title().lower().replace(" ", "_")}', elem_classes=['group-extension']) as group:
create_script_ui(script, inputs, inputs_alwayson)
try:
create_script_ui(script, inputs, inputs_alwayson)
except Exception as e:
errors.display(e, f'Create Script UI: type=builtin fn="{script.filename}"')
script.group = group
time_setup[script.title()] = time_setup.get(script.title(), 0) + (time.time()-t0)
@@ -528,7 +533,10 @@ class ScriptRunner:
continue
t0 = time.time()
with gr.Group(elem_id=f'{parent}_script_{script.title().lower().replace(" ", "_")}', elem_classes=['group-extension']) as group:
create_script_ui(script, inputs, inputs_alwayson)
try:
create_script_ui(script, inputs, inputs_alwayson)
except Exception as e:
errors.display(e, f'Create Script UI: type=extension fn="{script.filename}"')
script.group = group
time_setup[script.title()] = time_setup.get(script.title(), 0) + (time.time()-t0)
@@ -539,7 +547,10 @@ class ScriptRunner:
continue
with gr.Group(elem_id=f'{parent}_script_{script.title().lower().replace(" ", "_")}', elem_classes=['group-scripts'], visible=False) as group:
t0 = time.time()
create_script_ui(script, inputs, inputs_alwayson)
try:
create_script_ui(script, inputs, inputs_alwayson)
except Exception as e:
errors.display(e, f'Create Script UI: type=selectable fn="{script.filename}"')
time_setup[script.title()] = time_setup.get(script.title(), 0) + (time.time()-t0)
script.group = group
@@ -737,28 +748,42 @@ class ScriptRunner:
s.report()
def before_component(self, component: IOComponent, **kwargs):
if component is None or isinstance(component, gr.Blocks):
return
s = ScriptSummary('before-component')
for script in self.scripts:
if not hasattr(script, 'before_component'):
continue
for elem_id, callback in script.on_before_component_elem_id:
if elem_id == kwargs.get("elem_id"):
try:
callback(OnComponent(component=component))
except Exception as e:
errors.display(e, f"Running script before component: id={elem_id} fn={script.filename}")
try:
script.before_component(component, **kwargs)
except Exception as e:
errors.display(e, f'Running script before component: {script.filename}')
errors.display(e, f'Running script before component: fn={script.filename}')
s.record(script.title())
s.report()
def after_component(self, component: IOComponent, **kwargs):
if component is None or isinstance(component, gr.Blocks):
return
s = ScriptSummary('after-component')
for script in self.scripts:
if not hasattr(script, 'after_component'):
continue
for elem_id, callback in script.on_after_component_elem_id:
if elem_id == kwargs.get("elem_id"):
try:
callback(OnComponent(component=component))
except Exception as e:
errors.display(e, f"Running script before_component_elem_id: {script.filename}")
errors.display(e, f"Running script after component: id={elem_id} fn={script.filename}")
try:
script.after_component(component, **kwargs)
except Exception as e:
errors.display(e, f'Running script after component: {script.filename}')
errors.display(e, f'Running script after component: fn={script.filename}')
s.record(script.title())
s.report()
+4 -2
View File
@@ -929,12 +929,14 @@ class PromptEnhanceScript(scripts_manager.Script):
clear_btn = gr.Button(value='Clear', elem_id='prompt_enhance_clear', variant='secondary')
clear_btn.click(fn=lambda: '', inputs=[], outputs=[prompt_output])
copy_btn = gr.Button(value='Set prompt', elem_id='prompt_enhance_copy', variant='secondary')
copy_btn.click(fn=lambda x: x, inputs=[prompt_output], outputs=[self.prompt])
if self.prompt: # not registered for api script runner
copy_btn.click(fn=lambda x: x, inputs=[prompt_output], outputs=[self.prompt])
if self.image is None:
self.image = gr.Image(type='pil', interactive=False, visible=False, width=64, height=64) # dummy image
# Update vision toggle interactivity when model changes
llm_model.change(fn=self.update_vision_toggle, inputs=[llm_model], outputs=[use_vision], show_progress=False)
apply_btn.click(fn=self.apply, inputs=[self.prompt, self.image, apply_prompt, llm_model, prompt_system, prompt_prefix, prompt_suffix, max_tokens, do_sample, temperature, repetition_penalty, top_k, top_p, thinking_mode, nsfw_mode, use_vision, prefill_text, keep_prefill, keep_thinking], outputs=[prompt_output, self.prompt])
if self.prompt:
apply_btn.click(fn=self.apply, inputs=[self.prompt, self.image, apply_prompt, llm_model, prompt_system, prompt_prefix, prompt_suffix, max_tokens, do_sample, temperature, repetition_penalty, top_k, top_p, thinking_mode, nsfw_mode, use_vision, prefill_text, keep_prefill, keep_thinking], outputs=[prompt_output, self.prompt])
return [self.prompt, self.image, apply_auto, llm_model, prompt_system, prompt_prefix, prompt_suffix, max_tokens, do_sample, temperature, repetition_penalty, top_k, top_p, thinking_mode, nsfw_mode, use_vision, prefill_text, keep_prefill, keep_thinking]
def after_component(self, component, **_kwargs): # searching for actual ui prompt components