Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99ef3b6c52 | |||
| 65b6f8d3d5 | |||
| b57a816038 | |||
| 11f996a096 | |||
| ce0aab3643 | |||
| c251e8db8d | |||
| 284822323a | |||
| 1f59be5188 | |||
| cad87bf4e3 | |||
| 704628b903 | |||
| 636ff513b0 | |||
| 51206edb62 | |||
| c5934fb6e3 |
@@ -1,3 +1,22 @@
|
|||||||
|
## 1.5.1
|
||||||
|
|
||||||
|
### Minor:
|
||||||
|
* support parsing text encoder blocks in some new LoRAs
|
||||||
|
|
||||||
|
### Extensions and API:
|
||||||
|
* add postprocess_batch_list script callback
|
||||||
|
|
||||||
|
### Bug Fixes:
|
||||||
|
* fix reload altclip model error
|
||||||
|
* prepend the pythonpath instead of overriding it
|
||||||
|
* fix typo in SD_WEBUI_RESTARTING
|
||||||
|
* if txt2img/img2img raises an exception, finally call state.end()
|
||||||
|
* fix composable diffusion weight parsing
|
||||||
|
* restyle Startup profile for black users
|
||||||
|
* fix webui not launching with --nowebui
|
||||||
|
* catch exception for non git extensions
|
||||||
|
|
||||||
|
|
||||||
## 1.5.0
|
## 1.5.0
|
||||||
|
|
||||||
### Features:
|
### Features:
|
||||||
|
|||||||
@@ -163,6 +163,11 @@ def load_network(name, network_on_disk):
|
|||||||
key = key_network_without_network_parts.replace("lora_te1_text_model", "0_transformer_text_model")
|
key = key_network_without_network_parts.replace("lora_te1_text_model", "0_transformer_text_model")
|
||||||
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||||
|
|
||||||
|
# some SD1 Loras also have correct compvis keys
|
||||||
|
if sd_module is None:
|
||||||
|
key = key_network_without_network_parts.replace("lora_te1_text_model", "transformer_text_model")
|
||||||
|
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||||
|
|
||||||
if sd_module is None:
|
if sd_module is None:
|
||||||
keys_failed_to_match[key_network] = key
|
keys_failed_to_match[key_network] = key
|
||||||
continue
|
continue
|
||||||
|
|||||||
+22
-18
@@ -333,14 +333,16 @@ class Api:
|
|||||||
p.outpath_grids = opts.outdir_txt2img_grids
|
p.outpath_grids = opts.outdir_txt2img_grids
|
||||||
p.outpath_samples = opts.outdir_txt2img_samples
|
p.outpath_samples = opts.outdir_txt2img_samples
|
||||||
|
|
||||||
shared.state.begin(job="scripts_txt2img")
|
try:
|
||||||
if selectable_scripts is not None:
|
shared.state.begin(job="scripts_txt2img")
|
||||||
p.script_args = script_args
|
if selectable_scripts is not None:
|
||||||
processed = scripts.scripts_txt2img.run(p, *p.script_args) # Need to pass args as list here
|
p.script_args = script_args
|
||||||
else:
|
processed = scripts.scripts_txt2img.run(p, *p.script_args) # Need to pass args as list here
|
||||||
p.script_args = tuple(script_args) # Need to pass args as tuple here
|
else:
|
||||||
processed = process_images(p)
|
p.script_args = tuple(script_args) # Need to pass args as tuple here
|
||||||
shared.state.end()
|
processed = process_images(p)
|
||||||
|
finally:
|
||||||
|
shared.state.end()
|
||||||
|
|
||||||
b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else []
|
b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else []
|
||||||
|
|
||||||
@@ -390,14 +392,16 @@ class Api:
|
|||||||
p.outpath_grids = opts.outdir_img2img_grids
|
p.outpath_grids = opts.outdir_img2img_grids
|
||||||
p.outpath_samples = opts.outdir_img2img_samples
|
p.outpath_samples = opts.outdir_img2img_samples
|
||||||
|
|
||||||
shared.state.begin(job="scripts_img2img")
|
try:
|
||||||
if selectable_scripts is not None:
|
shared.state.begin(job="scripts_img2img")
|
||||||
p.script_args = script_args
|
if selectable_scripts is not None:
|
||||||
processed = scripts.scripts_img2img.run(p, *p.script_args) # Need to pass args as list here
|
p.script_args = script_args
|
||||||
else:
|
processed = scripts.scripts_img2img.run(p, *p.script_args) # Need to pass args as list here
|
||||||
p.script_args = tuple(script_args) # Need to pass args as tuple here
|
else:
|
||||||
processed = process_images(p)
|
p.script_args = tuple(script_args) # Need to pass args as tuple here
|
||||||
shared.state.end()
|
processed = process_images(p)
|
||||||
|
finally:
|
||||||
|
shared.state.end()
|
||||||
|
|
||||||
b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else []
|
b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else []
|
||||||
|
|
||||||
@@ -720,9 +724,9 @@ class Api:
|
|||||||
cuda = {'error': f'{err}'}
|
cuda = {'error': f'{err}'}
|
||||||
return models.MemoryResponse(ram=ram, cuda=cuda)
|
return models.MemoryResponse(ram=ram, cuda=cuda)
|
||||||
|
|
||||||
def launch(self, server_name, port):
|
def launch(self, server_name, port, root_path):
|
||||||
self.app.include_router(self.router)
|
self.app.include_router(self.router)
|
||||||
uvicorn.run(self.app, host=server_name, port=port, timeout_keep_alive=shared.cmd_opts.timeout_keep_alive)
|
uvicorn.run(self.app, host=server_name, port=port, timeout_keep_alive=shared.cmd_opts.timeout_keep_alive, root_path=root_path)
|
||||||
|
|
||||||
def kill_webui(self):
|
def kill_webui(self):
|
||||||
restart.stop_program()
|
restart.stop_program()
|
||||||
|
|||||||
@@ -56,9 +56,11 @@ class Extension:
|
|||||||
self.do_read_info_from_repo()
|
self.do_read_info_from_repo()
|
||||||
|
|
||||||
return self.to_dict()
|
return self.to_dict()
|
||||||
|
try:
|
||||||
d = cache.cached_data_for_file('extensions-git', self.name, os.path.join(self.path, ".git"), read_from_repo)
|
d = cache.cached_data_for_file('extensions-git', self.name, os.path.join(self.path, ".git"), read_from_repo)
|
||||||
self.from_dict(d)
|
self.from_dict(d)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
self.status = 'unknown'
|
self.status = 'unknown'
|
||||||
|
|
||||||
def do_read_info_from_repo(self):
|
def do_read_info_from_repo(self):
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ def run_extension_installer(extension_dir):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env['PYTHONPATH'] = os.path.abspath(".")
|
env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}"
|
||||||
|
|
||||||
print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env))
|
print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -233,7 +233,7 @@ def run_extensions_installers(settings_file):
|
|||||||
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")
|
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")
|
||||||
|
|
||||||
|
|
||||||
def requrements_met(requirements_file):
|
def requirements_met(requirements_file):
|
||||||
"""
|
"""
|
||||||
Does a simple parse of a requirements.txt file to determine if all rerqirements in it
|
Does a simple parse of a requirements.txt file to determine if all rerqirements in it
|
||||||
are already installed. Returns True if so, False if not installed or parsing fails.
|
are already installed. Returns True if so, False if not installed or parsing fails.
|
||||||
@@ -293,7 +293,7 @@ def prepare_environment():
|
|||||||
try:
|
try:
|
||||||
# the existance of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
|
# the existance of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
|
||||||
os.remove(os.path.join(script_path, "tmp", "restart"))
|
os.remove(os.path.join(script_path, "tmp", "restart"))
|
||||||
os.environ.setdefault('SD_WEBUI_RESTARTING ', '1')
|
os.environ.setdefault('SD_WEBUI_RESTARTING', '1')
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ def prepare_environment():
|
|||||||
if not os.path.isfile(requirements_file):
|
if not os.path.isfile(requirements_file):
|
||||||
requirements_file = os.path.join(script_path, requirements_file)
|
requirements_file = os.path.join(script_path, requirements_file)
|
||||||
|
|
||||||
if not requrements_met(requirements_file):
|
if not requirements_met(requirements_file):
|
||||||
run_pip(f"install -r \"{requirements_file}\"", "requirements")
|
run_pip(f"install -r \"{requirements_file}\"", "requirements")
|
||||||
|
|
||||||
run_extensions_installers(settings_file=args.ui_settings_file)
|
run_extensions_installers(settings_file=args.ui_settings_file)
|
||||||
|
|||||||
+25
-1
@@ -717,7 +717,27 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
|||||||
p.all_subseeds = [int(subseed) + x for x in range(len(p.all_prompts))]
|
p.all_subseeds = [int(subseed) + x for x in range(len(p.all_prompts))]
|
||||||
|
|
||||||
def infotext(iteration=0, position_in_batch=0, use_main_prompt=False):
|
def infotext(iteration=0, position_in_batch=0, use_main_prompt=False):
|
||||||
return create_infotext(p, p.all_prompts, p.all_seeds, p.all_subseeds, comments, iteration, position_in_batch, use_main_prompt)
|
all_prompts = p.all_prompts[:]
|
||||||
|
all_negative_prompts = p.all_negative_prompts[:]
|
||||||
|
all_seeds = p.all_seeds[:]
|
||||||
|
all_subseeds = p.all_subseeds[:]
|
||||||
|
|
||||||
|
# apply changes to generation data
|
||||||
|
all_prompts[iteration * p.batch_size:(iteration + 1) * p.batch_size] = p.prompts
|
||||||
|
all_negative_prompts[iteration * p.batch_size:(iteration + 1) * p.batch_size] = p.negative_prompts
|
||||||
|
all_seeds[iteration * p.batch_size:(iteration + 1) * p.batch_size] = p.seeds
|
||||||
|
all_subseeds[iteration * p.batch_size:(iteration + 1) * p.batch_size] = p.subseeds
|
||||||
|
|
||||||
|
# update p.all_negative_prompts in case extensions changed the size of the batch
|
||||||
|
# create_infotext below uses it
|
||||||
|
old_negative_prompts = p.all_negative_prompts
|
||||||
|
p.all_negative_prompts = all_negative_prompts
|
||||||
|
|
||||||
|
try:
|
||||||
|
return create_infotext(p, all_prompts, all_seeds, all_subseeds, comments, iteration, position_in_batch, use_main_prompt)
|
||||||
|
finally:
|
||||||
|
# restore p.all_negative_prompts in case extensions changed the size of the batch
|
||||||
|
p.all_negative_prompts = old_negative_prompts
|
||||||
|
|
||||||
if os.path.exists(cmd_opts.embeddings_dir) and not p.do_not_reload_embeddings:
|
if os.path.exists(cmd_opts.embeddings_dir) and not p.do_not_reload_embeddings:
|
||||||
model_hijack.embedding_db.load_textual_inversion_embeddings()
|
model_hijack.embedding_db.load_textual_inversion_embeddings()
|
||||||
@@ -806,6 +826,10 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
|||||||
if p.scripts is not None:
|
if p.scripts is not None:
|
||||||
p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n)
|
p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n)
|
||||||
|
|
||||||
|
postprocess_batch_list_args = scripts.PostprocessBatchListArgs(list(x_samples_ddim))
|
||||||
|
p.scripts.postprocess_batch_list(p, postprocess_batch_list_args, batch_number=n)
|
||||||
|
x_samples_ddim = postprocess_batch_list_args.images
|
||||||
|
|
||||||
for i, x_sample in enumerate(x_samples_ddim):
|
for i, x_sample in enumerate(x_samples_ddim):
|
||||||
p.batch_index = i
|
p.batch_index = i
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ def get_learned_conditioning(model, prompts: SdConditioning | list[str], steps):
|
|||||||
|
|
||||||
|
|
||||||
re_AND = re.compile(r"\bAND\b")
|
re_AND = re.compile(r"\bAND\b")
|
||||||
re_weight = re.compile(r"^(.*?)(?:\s*:\s*([-+]?(?:\d+\.?|\d*\.\d+)))?\s*$")
|
re_weight = re.compile(r"^((?:\s|.)*?)(?:\s*:\s*([-+]?(?:\d+\.?|\d*\.\d+)))?\s*$")
|
||||||
|
|
||||||
|
|
||||||
def get_multicond_prompt_list(prompts: SdConditioning | list[str]):
|
def get_multicond_prompt_list(prompts: SdConditioning | list[str]):
|
||||||
|
|||||||
+33
-1
@@ -16,6 +16,11 @@ class PostprocessImageArgs:
|
|||||||
self.image = image
|
self.image = image
|
||||||
|
|
||||||
|
|
||||||
|
class PostprocessBatchListArgs:
|
||||||
|
def __init__(self, images):
|
||||||
|
self.images = images
|
||||||
|
|
||||||
|
|
||||||
class Script:
|
class Script:
|
||||||
name = None
|
name = None
|
||||||
"""script's internal name derived from title"""
|
"""script's internal name derived from title"""
|
||||||
@@ -119,7 +124,7 @@ class Script:
|
|||||||
|
|
||||||
def after_extra_networks_activate(self, p, *args, **kwargs):
|
def after_extra_networks_activate(self, p, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Calledafter extra networks activation, before conds calculation
|
Called after extra networks activation, before conds calculation
|
||||||
allow modification of the network after extra networks activation been applied
|
allow modification of the network after extra networks activation been applied
|
||||||
won't be call if p.disable_extra_networks
|
won't be call if p.disable_extra_networks
|
||||||
|
|
||||||
@@ -156,6 +161,25 @@ class Script:
|
|||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Same as postprocess_batch(), but receives batch images as a list of 3D tensors instead of a 4D tensor.
|
||||||
|
This is useful when you want to update the entire batch instead of individual images.
|
||||||
|
|
||||||
|
You can modify the postprocessing object (pp) to update the images in the batch, remove images, add images, etc.
|
||||||
|
If the number of images is different from the batch size when returning,
|
||||||
|
then the script has the responsibility to also update the following attributes in the processing object (p):
|
||||||
|
- p.prompts
|
||||||
|
- p.negative_prompts
|
||||||
|
- p.seeds
|
||||||
|
- p.subseeds
|
||||||
|
|
||||||
|
**kwargs will have same items as process_batch, and also:
|
||||||
|
- batch_number - index of current batch, from 0 to number of batches-1
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
def postprocess_image(self, p, pp: PostprocessImageArgs, *args):
|
def postprocess_image(self, p, pp: PostprocessImageArgs, *args):
|
||||||
"""
|
"""
|
||||||
Called for every image after it has been generated.
|
Called for every image after it has been generated.
|
||||||
@@ -536,6 +560,14 @@ class ScriptRunner:
|
|||||||
except Exception:
|
except Exception:
|
||||||
errors.report(f"Error running postprocess_batch: {script.filename}", exc_info=True)
|
errors.report(f"Error running postprocess_batch: {script.filename}", exc_info=True)
|
||||||
|
|
||||||
|
def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, **kwargs):
|
||||||
|
for script in self.alwayson_scripts:
|
||||||
|
try:
|
||||||
|
script_args = p.script_args[script.args_from:script.args_to]
|
||||||
|
script.postprocess_batch_list(p, pp, *script_args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
errors.report(f"Error running postprocess_batch_list: {script.filename}", exc_info=True)
|
||||||
|
|
||||||
def postprocess_image(self, p, pp: PostprocessImageArgs):
|
def postprocess_image(self, p, pp: PostprocessImageArgs):
|
||||||
for script in self.alwayson_scripts:
|
for script in self.alwayson_scripts:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ class StableDiffusionModelHijack:
|
|||||||
ldm.modules.diffusionmodules.openaimodel.UNetModel.forward = sd_unet.UNetModel_forward
|
ldm.modules.diffusionmodules.openaimodel.UNetModel.forward = sd_unet.UNetModel_forward
|
||||||
|
|
||||||
def undo_hijack(self, m):
|
def undo_hijack(self, m):
|
||||||
if type(m.cond_stage_model) == xlmr.BertSeriesModelWithTransformation:
|
if type(m.cond_stage_model) == sd_hijack_xlmr.FrozenXLMREmbedderWithCustomWords:
|
||||||
m.cond_stage_model = m.cond_stage_model.wrapped
|
m.cond_stage_model = m.cond_stage_model.wrapped
|
||||||
|
|
||||||
elif type(m.cond_stage_model) == sd_hijack_clip.FrozenCLIPEmbedderWithCustomWords:
|
elif type(m.cond_stage_model) == sd_hijack_clip.FrozenCLIPEmbedderWithCustomWords:
|
||||||
|
|||||||
@@ -423,15 +423,16 @@ div#extras_scale_to_tab div.form{
|
|||||||
}
|
}
|
||||||
|
|
||||||
table.popup-table{
|
table.popup-table{
|
||||||
background: white;
|
background: var(--body-background-fill);
|
||||||
|
color: var(--body-text-color);
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
border: 4px solid white;
|
border: 4px solid var(--body-background-fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.popup-table td{
|
table.popup-table td{
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid rgba(128, 128, 128, 0.5);
|
||||||
max-width: 36em;
|
max-width: 36em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ def api_only():
|
|||||||
api.launch(
|
api.launch(
|
||||||
server_name="0.0.0.0" if cmd_opts.listen else "127.0.0.1",
|
server_name="0.0.0.0" if cmd_opts.listen else "127.0.0.1",
|
||||||
port=cmd_opts.port if cmd_opts.port else 7861,
|
port=cmd_opts.port if cmd_opts.port else 7861,
|
||||||
root_path = f"/{cmd_opts.subpath}"
|
root_path=f"/{cmd_opts.subpath}" if cmd_opts.subpath else ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -407,7 +407,7 @@ def webui():
|
|||||||
ssl_verify=cmd_opts.disable_tls_verify,
|
ssl_verify=cmd_opts.disable_tls_verify,
|
||||||
debug=cmd_opts.gradio_debug,
|
debug=cmd_opts.gradio_debug,
|
||||||
auth=gradio_auth_creds,
|
auth=gradio_auth_creds,
|
||||||
inbrowser=cmd_opts.autolaunch and os.getenv('SD_WEBUI_RESTARTING ') != '1',
|
inbrowser=cmd_opts.autolaunch and os.getenv('SD_WEBUI_RESTARTING') != '1',
|
||||||
prevent_thread_lock=True,
|
prevent_thread_lock=True,
|
||||||
allowed_paths=cmd_opts.gradio_allowed_path,
|
allowed_paths=cmd_opts.gradio_allowed_path,
|
||||||
app_kwargs={
|
app_kwargs={
|
||||||
|
|||||||
Reference in New Issue
Block a user