diff --git a/CHANGELOG.md b/CHANGELOG.md
index 855056dd4..72e71faec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,7 +17,8 @@
- quicksettings reset button to restore all quicksettings to default values
because things do sometimes get wrong...
- updated real-time hints, thanks @CalamitousFelicitousness
- - new *models -> list models* tab
+ - updated *models -> current* tab
+ - updated *models -> list models* tab
- more css optimizations and styling
- **Offloading**
- changed **default** values for offloading based on detected gpu memory
diff --git a/TODO.md b/TODO.md
index 0bbf4d8ae..f5641822c 100644
--- a/TODO.md
+++ b/TODO.md
@@ -33,8 +33,7 @@ Main ToDo list can be found at [GitHub projects](https://github.com/users/vladma
- Extensions tab:
- full CSS redesign
- Models tab:
- - Validate subtab: replace table with custom html
- - Update subtab: replace table with custom html
+ - Metadata subtab: replace table with custom html
- CivitAI subtab: redesign downloader
### Under Consideration
diff --git a/javascript/sdnext.css b/javascript/sdnext.css
index 4ac257c89..1396cf034 100644
--- a/javascript/sdnext.css
+++ b/javascript/sdnext.css
@@ -1965,6 +1965,30 @@ div:has(>#tab-gallery-folders) {
margin-top: 0.2em;
}
+#model_desc {
+ overflow: auto;
+}
+
+#model_list_table {
+ overflow: auto;
+ max-height: 50vh;
+}
+
+.simple-table tr {
+ vertical-align: baseline;
+}
+
+.simple-table td {
+ padding: 0.2em !important;
+}
+
+.model-config {
+ font-size: 0.8em !important;
+ opacity: 0.8;
+ max-height: 6em;
+ overflow-y: auto;
+}
+
@keyframes move {
from {
background-position-x: 0, -40px;
diff --git a/modules/modelstats.py b/modules/modelstats.py
index 8ef02ca35..4d60d34e5 100644
--- a/modules/modelstats.py
+++ b/modules/modelstats.py
@@ -11,6 +11,7 @@ class Module():
dtype: str = None
params: int = 0
modules: int = 0
+ quant: str = None
config: dict = None
def __init__(self, name, module):
@@ -25,6 +26,7 @@ class Module():
self.dtype = getattr(module, 'dtype', None)
self.params = sum(p.numel() for p in module.parameters(recurse=True))
self.modules = len(list(module.modules()))
+ self.quant = getattr(module, 'quantization_method', None)
def __repr__(self):
s = f'name="{self.name}" cls={self.cls} config={self.config is not None}'
@@ -69,6 +71,8 @@ class Model():
def analyze():
+ if not shared.sd_loaded:
+ return None
model = Model(shared.opts.sd_model_checkpoint)
if model.cls == '':
return model
diff --git a/modules/sd_offload.py b/modules/sd_offload.py
index 689c49e8c..c6156a3e8 100644
--- a/modules/sd_offload.py
+++ b/modules/sd_offload.py
@@ -351,6 +351,16 @@ def apply_balanced_offload_to_module(module, op="apply"):
devices.torch_gc(fast=True, force=True, reason='offload')
+def report_model_stats(module_name, module):
+ try:
+ size = offload_hook_instance.offload_map.get(module_name, 0)
+ quant = getattr(module, "quantization_method", None)
+ params = sum(p.numel() for p in module.parameters(recurse=True))
+ shared.log.debug(f'Module: name={module_name} cls={module.__class__.__name__} size={size:.3f} params={params} quant={quant}')
+ except Exception as e:
+ shared.log.error(f'Module stats: name={module_name} {e}')
+
+
def apply_balanced_offload(sd_model=None, exclude=[]):
global offload_hook_instance # pylint: disable=global-statement
if shared.opts.diffusers_offload_mode != "balanced":
@@ -382,6 +392,7 @@ def apply_balanced_offload(sd_model=None, exclude=[]):
module.module_name = module_name
module.offload_dir = os.path.join(shared.opts.accelerate_offload_path, checkpoint_name, module_name)
apply_balanced_offload_to_module(module, op='apply')
+ report_model_stats(module_name, module)
set_accelerate(sd_model)
t = time.time() - t0
process_timer.add('offload', t)
diff --git a/modules/ui_models.py b/modules/ui_models.py
index 42e9c86cc..d8c3ce4f5 100644
--- a/modules/ui_models.py
+++ b/modules/ui_models.py
@@ -16,7 +16,6 @@ def create_ui():
dummy_component = gr.Label(visible=False)
with gr.Row(elem_id="models_tab"):
with gr.Column(elem_id='models_output_container', scale=1):
- # models_output = gr.Textbox(elem_id="models_output", value="", show_label=False)
gr.HTML(elem_id="models_progress", value="")
models_image = gr.Image(elem_id="models_image", show_label=False, interactive=False, type='pil')
models_outcome = gr.HTML(elem_id="models_error", value="")
@@ -25,20 +24,48 @@ def create_ui():
with gr.Column(elem_id='models_input_container', scale=3):
with gr.Tab(label="Current"):
+ def create_modules_table(rows: list):
+ html = """
+
+
+ | Module | Class | Device | Dtype | Quant | Params | Modules | Config |
+
+
+ {tbody}
+
+
+ """
+ tbody = ''
+ for row in rows:
+ try:
+ config = str(row.config)
+ except Exception:
+ config = '{}'
+ try:
+ tbody += f"""
+
+ | {row.name} |
+ {row.cls} |
+ {row.device} |
+ {row.dtype} |
+ {row.quant} |
+ {row.params} |
+ {row.modules} |
+ {config} |
+
+ """
+ except Exception as e:
+ log.error(f'Model list: row={vars(row)} {e}')
+ return html.format(tbody=tbody)
+
def analyze():
from modules import modelstats
model = modelstats.analyze()
- desc = f"Model: {model.name}
Type: {model.type}
Class: {model.cls}
Size: {model.size} bytes
Modified: {model.mtime}
"
+ if model is None:
+ return ["Model not loaded", {}]
meta = model.meta
- components = []
- for m in model.modules:
- try:
- component = (m.name, m.cls, str(m.device), str(m.dtype), m.params, m.modules, str(m.config))
- components.append(component)
- except Exception:
- component = (m.name, m.cls, str(m.device), str(m.dtype), m.params, m.modules, '')
- components.append(component)
- return [desc, components, meta]
+ html = create_modules_table(model.modules)
+ return [html, meta]
with gr.Row():
gr.HTML(' Analyze currently loaded model
')
@@ -46,14 +73,100 @@ def create_ui():
model_analyze = gr.Button(value="Analyze", variant='primary')
with gr.Row():
model_desc = gr.HTML(value="", elem_id="model_desc")
- with gr.Row():
- module_headers = ['Module', 'Class', 'Device', 'DType', 'Params', 'Modules', 'Config']
- module_types = ['str', 'str', 'str', 'str', 'number', 'number', 'str']
- model_modules = gr.DataFrame(value=None, label=None, show_label=False, interactive=False, wrap=True, headers=module_headers, datatype=module_types, type='array')
with gr.Row():
model_meta = gr.JSON(label="Metadata", value={}, elem_id="model_meta")
- model_analyze.click(fn=analyze, inputs=[], outputs=[model_desc, model_modules, model_meta])
+ model_analyze.click(fn=analyze, inputs=[], outputs=[model_desc, model_meta])
+
+ with gr.Tab(label="List"):
+ def create_models_table(rows: list):
+ from modules import sd_detect
+ html = """
+
+
+ | Name | Type | Detect | Pipeline | Hash | Size | MTime |
+
+
+ {tbody}
+
+
+ """
+ tbody = ''
+ for row in rows:
+ try:
+ f = row.filename
+ stat = os.stat(row.filename)
+ if os.path.isfile(f):
+ typ = os.path.splitext(f)[1][1:]
+ size = f'{str(round(stat.st_size / 1024 / 1024 / 1024, 3)) + ' mb'}'
+ elif os.path.isdir(f):
+ typ = 'diffusers'
+ size = 'folder'
+ else:
+ typ = 'unknown'
+ size = 'unknown'
+ guess = 'Stable Diffusion XL' if 'XL' in f.upper() else 'Stable Diffusion' # set default guess
+ guess = sd_detect.guess_by_size(f, guess)
+ guess = sd_detect.guess_by_name(f, guess)
+ guess, pipeline = sd_detect.guess_by_diffusers(f, guess)
+ guess = sd_detect.guess_variant(f, guess)
+ pipeline = sd_detect.shared_items.get_pipelines().get(guess, None) if pipeline is None else pipeline
+ tbody += f"""
+
+ | {row.model_name} |
+ {typ} |
+ {guess} |
+ {pipeline.__name__ if pipeline else '(unknown)'} |
+ {row.shorthash} |
+ {size} |
+ {datetime.fromtimestamp(stat.st_mtime).replace(microsecond=0)} |
+
+ """
+ except Exception as e:
+ log.error(f'Model list: row={vars(row)} {e}')
+ return html.format(tbody=tbody)
+
+ with gr.Row():
+ gr.HTML(' List models
')
+ with gr.Row():
+ model_list_btn = gr.Button(value="List models", variant='primary')
+ model_checkhash_btn = gr.Button(value="Calculate missing hashes", variant='secondary')
+ model_checkhash_btn.click(fn=sd_models.update_model_hashes, inputs=[], outputs=[models_outcome])
+ with gr.Row():
+ model_table = gr.HTML(value='', elem_id="model_list_table")
+
+ model_list_btn.click(fn=lambda: create_models_table(sd_models.checkpoints_list.values()), inputs=[], outputs=[model_table])
+
+ with gr.Tab(label="Metadata"):
+ from modules.models_civitai import civit_search_metadata, civit_update_metadata, civit_update_select, civit_update_download
+ with gr.Row():
+ gr.HTML(' CivitAI fetch metadata
')
+ gr.HTML('Fetches preview and metadata information for models with missing information
Models with existing previews and information are not updated
')
+ with gr.Row():
+ civit_previews_btn = gr.Button(value="Start", variant='primary')
+ with gr.Row():
+ civit_previews_rehash = gr.Checkbox(value=True, label="Check alternative hash")
+ civit_previews_btn.click(fn=civit_search_metadata, inputs=[civit_previews_rehash, civit_previews_rehash], outputs=[models_outcome])
+
+ with gr.Row():
+ gr.HTML(' Scan CivitAI for information on latest available model versions
')
+ with gr.Row():
+ civit_update_btn = gr.Button(value="Update", variant='primary')
+ with gr.Row():
+ gr.HTML('Update scan results
')
+ with gr.Row():
+ civit_headers4 = ['ID', 'File', 'Name', 'Versions', 'Current', 'Latest', 'Update']
+ civit_types4 = ['number', 'str', 'str', 'number', 'str', 'str', 'str']
+ civit_widths4 = ['10%', '25%', '25%', '5%', '10%', '10%', '15%']
+ civit_results4 = gr.DataFrame(value=None, label=None, show_label=False, interactive=False, wrap=True, row_count=20, headers=civit_headers4, datatype=civit_types4, type='array', column_widths=civit_widths4)
+ with gr.Row():
+ gr.HTML('Select model from the list and download update if available
')
+ with gr.Row():
+ civit_update_download_btn = gr.Button(value="Download", variant='primary', visible=False)
+
+ civit_update_btn.click(fn=civit_update_metadata, inputs=[], outputs=[civit_results4, models_outcome])
+ civit_results4.select(fn=civit_update_select, inputs=[civit_results4], outputs=[models_outcome, civit_update_download_btn])
+ civit_update_download_btn.click(fn=civit_update_download, inputs=[], outputs=[models_outcome])
with gr.Tab(label="Loader"):
from modules import ui_models_load
@@ -303,7 +416,7 @@ def create_ui():
]
)
- with gr.Tab(label="Modules"):
+ with gr.Tab(label="Replace"):
with gr.Row():
gr.HTML(' Replace model components
')
with gr.Row():
@@ -375,105 +488,6 @@ def create_ui():
outputs=[models_outcome]
)
- with gr.Tab(label="List"):
- from modules import sd_checkpoint
- def create_models_table(rows: list[sd_checkpoint.CheckpointInfo]):
- from modules import sd_detect
- html = """
-
-
-
- | Name |
- Type |
- Detect |
- Pipeline |
- Hash |
- Size |
- MTime |
-
-
-
- {tbody}
-
-
- """
- tbody = ''
- for row in rows:
- try:
- f = row.filename
- stat = os.stat(row.filename)
- if os.path.isfile(f):
- typ = os.path.splitext(f)[1][1:]
- size = f'{str(round(stat.st_size / 1024 / 1024 / 1024, 3)) + ' mb'}'
- elif os.path.isdir(f):
- typ = 'diffusers'
- size = 'folder'
- else:
- typ = 'unknown'
- size = 'unknown'
- guess = 'Stable Diffusion XL' if 'XL' in f.upper() else 'Stable Diffusion' # set default guess
- guess = sd_detect.guess_by_size(f, guess)
- guess = sd_detect.guess_by_name(f, guess)
- guess, pipeline = sd_detect.guess_by_diffusers(f, guess)
- guess = sd_detect.guess_variant(f, guess)
- pipeline = sd_detect.shared_items.get_pipelines().get(guess, None) if pipeline is None else pipeline
- tbody += f"""
-
- | {row.model_name} |
- {typ} |
- {guess} |
- {pipeline.__name__ if pipeline else '(unknown)'} |
- {row.shorthash} |
- {size} |
- {datetime.fromtimestamp(stat.st_mtime).replace(microsecond=0)} |
-
- """
- except Exception as e:
- log.error(f'Model list: row={vars(row)} {e}')
- return html.format(tbody=tbody)
-
- with gr.Row():
- gr.HTML(' List models
')
- with gr.Row():
- model_list_btn = gr.Button(value="List models", variant='primary')
- model_checkhash_btn = gr.Button(value="Calculate missing hashes", variant='secondary')
- model_checkhash_btn.click(fn=sd_models.update_model_hashes, inputs=[], outputs=[models_outcome])
- with gr.Row():
- model_table = gr.HTML(value='', elem_id="model_list_table")
-
- model_list_btn.click(fn=lambda: create_models_table(sd_models.checkpoints_list.values()), inputs=[], outputs=[model_table])
-
- with gr.Tab(label="Metadata"):
- from modules.models_civitai import civit_search_metadata, civit_update_metadata, civit_update_select, civit_update_download
- with gr.Row():
- gr.HTML(' CivitAI fetch metadata
')
- gr.HTML('Fetches preview and metadata information for models with missing information
Models with existing previews and information are not updated
')
- with gr.Row():
- civit_previews_btn = gr.Button(value="Start", variant='primary')
- with gr.Row():
- civit_previews_rehash = gr.Checkbox(value=True, label="Check alternative hash")
- civit_previews_btn.click(fn=civit_search_metadata, inputs=[civit_previews_rehash, civit_previews_rehash], outputs=[models_outcome])
-
- with gr.Row():
- gr.HTML(' Scan CivitAI for information on latest available model versions
')
- with gr.Row():
- civit_update_btn = gr.Button(value="Update", variant='primary')
- with gr.Row():
- gr.HTML('Update scan results
')
- with gr.Row():
- civit_headers4 = ['ID', 'File', 'Name', 'Versions', 'Current', 'Latest', 'Update']
- civit_types4 = ['number', 'str', 'str', 'number', 'str', 'str', 'str']
- civit_widths4 = ['10%', '25%', '25%', '5%', '10%', '10%', '15%']
- civit_results4 = gr.DataFrame(value=None, label=None, show_label=False, interactive=False, wrap=True, row_count=20, headers=civit_headers4, datatype=civit_types4, type='array', column_widths=civit_widths4)
- with gr.Row():
- gr.HTML('Select model from the list and download update if available
')
- with gr.Row():
- civit_update_download_btn = gr.Button(value="Download", variant='primary', visible=False)
-
- civit_update_btn.click(fn=civit_update_metadata, inputs=[], outputs=[civit_results4, models_outcome])
- civit_results4.select(fn=civit_update_select, inputs=[civit_results4], outputs=[models_outcome, civit_update_download_btn])
- civit_update_download_btn.click(fn=civit_update_download, inputs=[], outputs=[models_outcome])
-
with gr.Tab(label="CivitAI"):
from modules.models_civitai import civitai_update_token, civit_search_model, civit_search_metadata, civit_select1, civit_select2, civit_select3, civit_download_model