├── .gitignore ├── LICENSE ├── README.md ├── benchmark.jpg ├── benchmark.py ├── javascript ├── jquery.js ├── sparkline.js └── system-info.js ├── pages ├── .eslintrc.json ├── README.md ├── benchmark.css ├── benchmark.html ├── favicon.ico ├── favicon.png ├── manifest.webmanifest ├── package-lock.json ├── package.json ├── pnpm-lock.yaml └── roboto.ttf ├── scripts └── system-info.py ├── style.css ├── system-info-json.jpg ├── system-info-models.jpg └── system-info.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | __pycache__ 3 | benchmark-data-local.json 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Vladimir Mandic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # System Info tab extensions for SD Automatic WebUI 2 | 3 | Creates a top-level **System Info** tab in Automatic WebUI with 4 | 5 | *Note*: 6 | 7 | - State & memory info are auto-updated every second if tab is visible 8 | (no updates are performed when tab is not visible) 9 | - All other information is updated once upon WebUI load and 10 | can be force refreshed if required 11 | 12 | ## Current information 13 | 14 | - Server start time 15 | - Version 16 | - Current Model & VAE 17 | - Current State 18 | - Current Memory statistics 19 | 20 | ## System data 21 | 22 | - Platform details 23 | - Torch, CUDA and GPU details 24 | - Active CMD flags such as `low-vram` or `med-vram` 25 | - Versions of critical libraries as `xformers`, `transformers`, etc. 26 | - Versions of dependent repositories such as `k-diffusion`, etc. 27 | 28 | ![screenshot](system-info.jpg) 29 | 30 | ## Benchmark 31 | 32 | - Allows to run standardized benchmark and optionally submit data to cloud logger [[details]](pages/README.md) 33 | 34 | ![screenshot](benchmark.jpg) 35 | 36 | - *Note: Record is appended if any of the system properties change else benchmark data replaces existing matching record* 37 | 38 | - All results can be [viewed online](https://vladmandic.github.io/sd-extension-system-info/pages/benchmark.html) 39 | 40 | ## Models 41 | 42 | - Models (with hash) 43 | - Hypernetworks 44 | - Embeddings (including info on number of vectors per embedding) 45 | 46 | ![screenshot](system-info-models.jpg) 47 | 48 | ## Info Object 49 | 50 | - System object is available as JSON for quick passing of information 51 | 52 | ![screenshot](system-info-json.jpg) 53 | -------------------------------------------------------------------------------- /benchmark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladmandic/sd-extension-system-info/539625f289c93555c15f563fbbcb3e4916feb67c/benchmark.jpg -------------------------------------------------------------------------------- /benchmark.py: -------------------------------------------------------------------------------- 1 | import time 2 | import logging 3 | from modules import shared 4 | from modules.processing import StableDiffusionProcessingTxt2Img, Processed, process_images 5 | 6 | 7 | log = logging.getLogger('sd') 8 | 9 | default_args = { 10 | # 'sd_model': None, 11 | 'prompt': 'postapocalyptic steampunk city, exploration, cinematic, realistic, hyper detailed, photorealistic maximum detail, volumetric light, (((focus))), wide-angle, (((brightly lit))), (((vegetation))), lightning, vines, destruction, devastation, wartorn, ruins', 12 | 'sampler_name': 'Euler a', 13 | 'batch_size': 1, 14 | 'n_iter': 1, 15 | 'steps': 10, 16 | 'cfg_scale': 7.0, 17 | 'width': 512, 18 | 'height': 512, 19 | 'do_not_save_samples': True, 20 | 'do_not_save_grid': True, 21 | 'negative_prompt': '(((blurry))), ((foggy)), (((dark))), ((monochrome)), sun, (((depth of field)))', 22 | 'do_not_reload_embeddings': True 23 | } 24 | args = {} 25 | 26 | 27 | def run_benchmark(batch: int, steps: str, width: int, height: int): 28 | global args # pylint: disable=global-statement 29 | shared.state.begin('Benchmark') 30 | args = default_args.copy() 31 | # if args['sd_model'] is None: 32 | # args['sd_model'] = shared.sd_model 33 | args['batch_size'] = batch 34 | if steps == 'turbo': 35 | args['steps'] = 10 36 | elif steps == 'long': 37 | args['steps'] = 50 38 | else: 39 | args['steps'] = 20 40 | if width and width > 0: 41 | args['width'] = int(width) 42 | if height and height > 0: 43 | args['height'] = int(height) 44 | mp = 0 45 | p = StableDiffusionProcessingTxt2Img(**args) 46 | t0 = time.time() 47 | try: 48 | processed: Processed = process_images(p) 49 | if processed is None or processed.images is None: 50 | log.error(f'SD-System-Info benchmark error: {batch} no results') 51 | return 'error' 52 | if len(processed.images) != batch: 53 | log.error(f'SD-System-Info benchmark error: {batch} results mismatch') 54 | return 'error' 55 | for image in processed.images: 56 | if image.width * image.height < 65536: 57 | log.error(f'SD-System-Info benchmark error: {batch} image too small') 58 | return 'error' 59 | mp += image.width * image.height 60 | except Exception as e: 61 | log.error(f'SD-System-Info benchmark error: {batch} {e}') 62 | from modules import errors 63 | errors.display(e, 'Benchmark') 64 | return 'error' 65 | t1 = time.time() 66 | shared.state.end() 67 | its = args['steps'] * batch / (t1 - t0) 68 | mp = mp / 1024 / 1024 69 | mps = mp / (t1 - t0) 70 | log.debug(f'SD-System-Info benchmark: batch={batch} time={t1-t0:.2f} steps={args["steps"]} its={its:.2f} mps={mps:.2f}') 71 | if its > 300: 72 | log.error(f'SD-System-Info benchmark: its={its:.2f} too high') 73 | return 'error' 74 | return round(its, 2) 75 | 76 | 77 | class LogFilter(logging.Filter): 78 | import socket 79 | hostname = socket.gethostname() 80 | def filter(self, record): 81 | record.hostname = LogFilter.hostname 82 | return True 83 | 84 | 85 | def submit_benchmark(data, username): 86 | from logging.handlers import SysLogHandler 87 | from hashlib import sha256 88 | 89 | syslog = SysLogHandler(address=('logs3.papertrailapp.com', 32554)) 90 | syslog.addFilter(LogFilter()) 91 | formatter = logging.Formatter(f'%(asctime)s %(hostname)s SDBENCHMARK: {username} %(message)s', datefmt='%b %d %H:%M:%S') 92 | syslog.setFormatter(formatter) 93 | remote = logging.getLogger('SDBENCHMARK') 94 | remote.setLevel(logging.INFO) 95 | for h in remote.handlers: # remove local handlers 96 | remote.removeHandler(h) 97 | remote.addHandler(syslog) 98 | for line in data: 99 | message = '|'.join(line).replace(' ', ' ').replace('"', '').strip() 100 | hash256 = sha256(message.encode('utf-8')).hexdigest()[:6] 101 | message = message + '|' + hash256 102 | remote.info(message) 103 | -------------------------------------------------------------------------------- /javascript/sparkline.js: -------------------------------------------------------------------------------- 1 | /* jquery.sparkline 2.1.2 - http://omnipotent.net/jquery.sparkline/ 2 | ** Licensed under the New BSD License - see above site for details */ 3 | 4 | (function(a,b,c){(function(a){typeof define=="function"&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.sparkline&&a(jQuery)})(function(d){"use strict";var e={},f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L=0;f=function(){return{common:{type:"line",lineColor:"#00f",fillColor:"#cdf",defaultPixelsPerValue:3,width:"auto",height:"auto",composite:!1,tagValuesAttribute:"values",tagOptionsPrefix:"spark",enableTagOptions:!1,enableHighlight:!0,highlightLighten:1.4,tooltipSkipNull:!0,tooltipPrefix:"",tooltipSuffix:"",disableHiddenCheck:!1,numberFormatter:!1,numberDigitGroupCount:3,numberDigitGroupSep:",",numberDecimalMark:".",disableTooltips:!1,disableInteraction:!1},line:{spotColor:"#f80",highlightSpotColor:"#5f5",highlightLineColor:"#f22",spotRadius:1.5,minSpotColor:"#f80",maxSpotColor:"#f80",lineWidth:1,normalRangeMin:c,normalRangeMax:c,normalRangeColor:"#ccc",drawNormalOnTop:!1,chartRangeMin:c,chartRangeMax:c,chartRangeMinX:c,chartRangeMaxX:c,tooltipFormat:new h(' {{prefix}}{{y}}{{suffix}}')},bar:{barColor:"#3366cc",negBarColor:"#f44",stackedBarColor:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],zeroColor:c,nullColor:c,zeroAxis:!0,barWidth:4,barSpacing:1,chartRangeMax:c,chartRangeMin:c,chartRangeClip:!1,colorMap:c,tooltipFormat:new h(' {{prefix}}{{value}}{{suffix}}')},tristate:{barWidth:4,barSpacing:1,posBarColor:"#6f6",negBarColor:"#f44",zeroBarColor:"#999",colorMap:{},tooltipFormat:new h(' {{value:map}}'),tooltipValueLookups:{map:{"-1":"Loss",0:"Draw",1:"Win"}}},discrete:{lineHeight:"auto",thresholdColor:c,thresholdValue:0,chartRangeMax:c,chartRangeMin:c,chartRangeClip:!1,tooltipFormat:new h("{{prefix}}{{value}}{{suffix}}")},bullet:{targetColor:"#f33",targetWidth:3,performanceColor:"#33f",rangeColors:["#d3dafe","#a8b6ff","#7f94ff"],base:c,tooltipFormat:new h("{{fieldkey:fields}} - {{value}}"),tooltipValueLookups:{fields:{r:"Range",p:"Performance",t:"Target"}}},pie:{offset:0,sliceColors:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],borderWidth:0,borderColor:"#000",tooltipFormat:new h(' {{value}} ({{percent.1}}%)')},box:{raw:!1,boxLineColor:"#000",boxFillColor:"#cdf",whiskerColor:"#000",outlierLineColor:"#333",outlierFillColor:"#fff",medianColor:"#f00",showOutliers:!0,outlierIQR:1.5,spotRadius:1.5,target:c,targetColor:"#4a2",chartRangeMax:c,chartRangeMin:c,tooltipFormat:new h("{{field:fields}}: {{value}}"),tooltipFormatFieldlistKey:"field",tooltipValueLookups:{fields:{lq:"Lower Quartile",med:"Median",uq:"Upper Quartile",lo:"Left Outlier",ro:"Right Outlier",lw:"Left Whisker",rw:"Right Whisker"}}}}},E='.jqstooltip { position: absolute;left: 0px;top: 0px;visibility: hidden;background: rgb(0, 0, 0) transparent;background-color: rgba(0,0,0,0.6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";color: white;font: 10px arial, san serif;text-align: left;white-space: nowrap;padding: 5px;border: 1px solid white;z-index: 10000;}.jqsfield { color: white;font: 10px arial, san serif;text-align: left;}',g=function(){var a,b;return a=function(){this.init.apply(this,arguments)},arguments.length>1?(arguments[0]?(a.prototype=d.extend(new arguments[0],arguments[arguments.length-1]),a._super=arguments[0].prototype):a.prototype=arguments[arguments.length-1],arguments.length>2&&(b=Array.prototype.slice.call(arguments,1,-1),b.unshift(a.prototype),d.extend.apply(d,b))):a.prototype=arguments[0],a.prototype.cls=a,a},d.SPFormatClass=h=g({fre:/\{\{([\w.]+?)(:(.+?))?\}\}/g,precre:/(\w+)\.(\d+)/,init:function(a,b){this.format=a,this.fclass=b},render:function(a,b,d){var e=this,f=a,g,h,i,j,k;return this.format.replace(this.fre,function(){var a;return h=arguments[1],i=arguments[3],g=e.precre.exec(h),g?(k=g[2],h=g[1]):k=!1,j=f[h],j===c?"":i&&b&&b[i]?(a=b[i],a.get?b[i].get(j)||j:b[i][j]||j):(n(j)&&(d.get("numberFormatter")?j=d.get("numberFormatter")(j):j=s(j,k,d.get("numberDigitGroupCount"),d.get("numberDigitGroupSep"),d.get("numberDecimalMark"))),j)})}}),d.spformat=function(a,b){return new h(a,b)},i=function(a,b,c){return ac?c:a},j=function(a,c){var d;return c===2?(d=b.floor(a.length/2),a.length%2?a[d]:(a[d-1]+a[d])/2):a.length%2?(d=(a.length*c+c)/4,d%1?(a[b.floor(d)]+a[b.floor(d)-1])/2:a[d-1]):(d=(a.length*c+2)/4,d%1?(a[b.floor(d)]+a[b.floor(d)-1])/2:a[d-1])},k=function(a){var b;switch(a){case"undefined":a=c;break;case"null":a=null;break;case"true":a=!0;break;case"false":a=!1;break;default:b=parseFloat(a),a==b&&(a=b)}return a},l=function(a){var b,c=[];for(b=a.length;b--;)c[b]=k(a[b]);return c},m=function(a,b){var c,d,e=[];for(c=0,d=a.length;c0;h-=c)a.splice(h,0,e);return a.join("")},o=function(a,b,c){var d;for(d=b.length;d--;){if(c&&b[d]===null)continue;if(b[d]!==a)return!1}return!0},p=function(a){var b=0,c;for(c=a.length;c--;)b+=typeof a[c]=="number"?a[c]:0;return b},r=function(a){return d.isArray(a)?a:[a]},q=function(b){var c;a.createStyleSheet?a.createStyleSheet().cssText=b:(c=a.createElement("style"),c.type="text/css",a.getElementsByTagName("head")[0].appendChild(c),c[typeof a.body.style.WebkitAppearance=="string"?"innerText":"innerHTML"]=b)},d.fn.simpledraw=function(b,e,f,g){var h,i;if(f&&(h=this.data("_jqs_vcanvas")))return h;if(d.fn.sparkline.canvas===!1)return!1;if(d.fn.sparkline.canvas===c){var j=a.createElement("canvas");if(!j.getContext||!j.getContext("2d")){if(!a.namespaces||!!a.namespaces.v)return d.fn.sparkline.canvas=!1,!1;a.namespaces.add("v","urn:schemas-microsoft-com:vml","#default#VML"),d.fn.sparkline.canvas=function(a,b,c,d){return new J(a,b,c)}}else d.fn.sparkline.canvas=function(a,b,c,d){return new I(a,b,c,d)}}return b===c&&(b=d(this).innerWidth()),e===c&&(e=d(this).innerHeight()),h=d.fn.sparkline.canvas(b,e,this,g),i=d(this).data("_jqs_mhandler"),i&&i.registerCanvas(h),h},d.fn.cleardraw=function(){var a=this.data("_jqs_vcanvas");a&&a.reset()},d.RangeMapClass=t=g({init:function(a){var b,c,d=[];for(b in a)a.hasOwnProperty(b)&&typeof b=="string"&&b.indexOf(":")>-1&&(c=b.split(":"),c[0]=c[0].length===0?-Infinity:parseFloat(c[0]),c[1]=c[1].length===0?Infinity:parseFloat(c[1]),c[2]=a[b],d.push(c));this.map=a,this.rangelist=d||!1},get:function(a){var b=this.rangelist,d,e,f;if((f=this.map[a])!==c)return f;if(b)for(d=b.length;d--;){e=b[d];if(e[0]<=a&&e[1]>=a)return e[2]}return c}}),d.range_map=function(a){return new t(a)},u=g({init:function(a,b){var c=d(a);this.$el=c,this.options=b,this.currentPageX=0,this.currentPageY=0,this.el=a,this.splist=[],this.tooltip=null,this.over=!1,this.displayTooltips=!b.get("disableTooltips"),this.highlightEnabled=!b.get("disableHighlight")},registerSparkline:function(a){this.splist.push(a),this.over&&this.updateDisplay()},registerCanvas:function(a){var b=d(a.canvas);this.canvas=a,this.$canvas=b,b.mouseenter(d.proxy(this.mouseenter,this)),b.mouseleave(d.proxy(this.mouseleave,this)),b.click(d.proxy(this.mouseclick,this))},reset:function(a){this.splist=[],this.tooltip&&a&&(this.tooltip.remove(),this.tooltip=c)},mouseclick:function(a){var b=d.Event("sparklineClick");b.originalEvent=a,b.sparklines=this.splist,this.$el.trigger(b)},mouseenter:function(b){d(a.body).unbind("mousemove.jqs"),d(a.body).bind("mousemove.jqs",d.proxy(this.mousemove,this)),this.over=!0,this.currentPageX=b.pageX,this.currentPageY=b.pageY,this.currentEl=b.target,!this.tooltip&&this.displayTooltips&&(this.tooltip=new v(this.options),this.tooltip.updatePosition(b.pageX,b.pageY)),this.updateDisplay()},mouseleave:function(){d(a.body).unbind("mousemove.jqs");var b=this.splist,c=b.length,e=!1,f,g;this.over=!1,this.currentEl=null,this.tooltip&&(this.tooltip.remove(),this.tooltip=null);for(g=0;g