M05: Override isolation / temporary opts seam

- Add modules/runtime_utils.py with temporary_opts context manager
- Refactor process_images() to use temporary_opts for override application
- Preserve opts.set(is_api=True, run_callbacks=False) and setattr restore
- Add test/quality/test_opts_override.py (samples_save, restore_afterwards)
- Model/VAE reload and token merging remain in process_images per decisions

Made-with: Cursor
This commit is contained in:
Michael Cahill 2026-03-09 14:14:13 -07:00
parent 9952611b02
commit 5fe8245957
5 changed files with 424 additions and 62 deletions

View file

@ -26,6 +26,7 @@ import modules.paths as paths
import modules.face_restoration
import modules.images as images
import modules.styles
import modules.runtime_utils as runtime_utils
import modules.sd_models as sd_models
import modules.sd_vae as sd_vae
from ldm.data.util import AddMiDaS
@ -820,42 +821,32 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
if p.scripts is not None:
p.scripts.before_process(p)
stored_opts = {k: opts.data[k] if k in opts.data else opts.get_default(k) for k in p.override_settings.keys() if k in opts.data}
# if no checkpoint override or the override checkpoint can't be found, remove override entry and load opts checkpoint
# and if after running refiner, the refiner model is not unloaded - webui swaps back to main model here, if model over is present it will be reloaded afterwards
if sd_models.checkpoint_aliases.get(p.override_settings.get('sd_model_checkpoint')) is None:
p.override_settings.pop('sd_model_checkpoint', None)
sd_models.reload_model_weights()
try:
# if no checkpoint override or the override checkpoint can't be found, remove override entry and load opts checkpoint
# and if after running refiner, the refiner model is not unloaded - webui swaps back to main model here, if model over is present it will be reloaded afterwards
if sd_models.checkpoint_aliases.get(p.override_settings.get('sd_model_checkpoint')) is None:
p.override_settings.pop('sd_model_checkpoint', None)
sd_models.reload_model_weights()
with runtime_utils.temporary_opts(p.override_settings, restore_afterwards=p.override_settings_restore_afterwards):
for k in p.override_settings:
if k == 'sd_model_checkpoint':
sd_models.reload_model_weights()
if k == 'sd_vae':
sd_vae.reload_vae_weights()
for k, v in p.override_settings.items():
opts.set(k, v, is_api=True, run_callbacks=False)
sd_models.apply_token_merging(p.sd_model, p.get_token_merging_ratio())
if k == 'sd_model_checkpoint':
sd_models.reload_model_weights()
if k == 'sd_vae':
sd_vae.reload_vae_weights()
sd_models.apply_token_merging(p.sd_model, p.get_token_merging_ratio())
# backwards compatibility, fix sampler and scheduler if invalid
sd_samplers.fix_p_invalid_sampler_and_scheduler(p)
with profiling.Profiler():
res = process_images_inner(p)
# backwards compatibility, fix sampler and scheduler if invalid
sd_samplers.fix_p_invalid_sampler_and_scheduler(p)
with profiling.Profiler():
res = process_images_inner(p)
finally:
sd_models.apply_token_merging(p.sd_model, 0)
# restore opts to original state
if p.override_settings_restore_afterwards:
for k, v in stored_opts.items():
setattr(opts, k, v)
if k == 'sd_vae':
sd_vae.reload_vae_weights()
if p.override_settings_restore_afterwards and 'sd_vae' in p.override_settings:
sd_vae.reload_vae_weights()
return res