AUTOMATIC1111/stable-diffusion-webui
Patterns and conventions
A reference of recurring patterns in the codebase. New code should follow these unless there's a specific reason not to.
Module shape
- One responsibility per module under
modules/. There is no package layering: every module is a sibling. - Module names follow
snake_case. Private helpers are prefixed with_. - Each module imports the small shared globals it needs from
modules/shared.py:cmd_opts,opts,state,sd_model,sd_upscalers,face_restorers.sharedis loaded early and is the project's de-facto service locator.
from modules.shared import opts, cmd_opts, state
import modules.shared as shared
# Use shared.sd_model for "the currently loaded checkpoint"
# Use opts.SOMETHING for user-editable settings
# Use cmd_opts.something for command-line flags
# Use shared.state for progress / interruptThe p convention
Any function that touches generation accepts a p: StableDiffusionProcessing. Scripts mutate p in place:
def process(self, p, *args):
p.extra_generation_params["My setting"] = args[0]
p.steps = max(p.steps, 10)Extending the param dict via p.extra_generation_params makes the value appear in the saved infotext. Adding fields directly to p is fine, but they won't survive serialisation.
Settings
User-editable options are declared in modules/shared_options.py by appending to options_templates:
options_templates.update(options_section(("category", "Category Label"), {
"my_setting": OptionInfo(default_value, "Label", gr.Component, {"...": ...}).info("Tooltip"),
}))options_section, OptionInfo and friends are in modules/options.py. Extensions should register settings inside an on_ui_settings callback (see systems/script-callbacks.md), so they don't pollute the core list.
Read with opts.my_setting. Write with opts.set("my_setting", value). Always read at use-time, never cache the value at import-time — the user can change settings while the server is running.
Error handling
The repo deliberately swallows errors that originate in user-supplied code (extensions, scripts, custom YAML configs) so a single broken extension doesn't crash the whole server:
from modules import errors
try:
do_something_extension()
except Exception:
errors.report(f"Error in {extension.name}", exc_info=True)errors.report in modules/errors.py prints a banner header and the traceback. For errors in core code, just raise — the wrapping wrap_gradio_call (in modules/call_queue.py) will convert it to a user-visible Gradio error.
Hijacking is the extension mechanism for ML code
The ldm (Latent Diffusion Model) package is vendored from upstream and not modified directly. Instead, modules/sd_hijack.py and friends monkey-patch its classes at runtime:
model_hijack.hijack(sd_model)swaps in custom CLIP wrappers, optimised attention, and prompt parsing.model_hijack.undo_hijack(sd_model)reverses the changes when the model is unloaded.- All hijacks live under
modules/sd_hijack_*.pyand are documented in systems/sd-hijack.md.
When adding new ML behaviour, prefer hijacks over forking ldm.
Prompt mutation: extra_networks
If your script needs to consume new <my:foo:1.0> syntax in prompts, register an ExtraNetwork subclass in modules/extra_networks.py. The framework parses these tokens out of the prompt before encoding and passes them to your activate(p, params_list) and deactivate(p) methods. The Lora extension is the canonical example — see systems/extra-networks.md.
Building UI components
Most UI work happens in modules/ui.py and the ui_*.py modules. Conventions:
- Wrap groups of related controls in
gr.Accordion(label, open=False)to keep the page compact. - For settings that round-trip via PNG-info, append the
(component, key)pair to the localpaste_fieldslist — this is how the "Read generation parameters" button works. - Use
ui_components.FormRow,FormColumn,FormGroup(inmodules/ui_components.py) for compact form layouts. - Use
InputAccordion(also inui_components.py) when the accordion's "open" state should itself be a parameter sent through the API. - Keep custom JavaScript in
javascript/and reference it from a Gradio component via_js="myFunctionName". Files injavascript/are auto-loaded on page load.
The wrap_gradio_call convention
Every event handler that does GPU work or accesses shared.state is wrapped with one of:
| Wrapper | Purpose |
|---|---|
wrap_gradio_call |
Catches exceptions; turns them into a user-visible error |
wrap_gradio_gpu_call |
Adds the call queue lock so only one GPU job runs at a time |
wrap_gradio_call_no_job |
Catches exceptions but doesn't update state.job (added in v1.10 to fix progress bar resets) |
All three live in modules/call_queue.py. New event handlers should use the appropriate wrapper.
Lazy imports
Heavy modules — torch, safetensors, gradio, transformers — are imported lazily inside functions wherever practical. This keeps python launch.py --help and --dump-sysinfo fast and lets the launch script print its banner before any GPU work happens. When adding a new dependency, follow the same pattern unless the code path always uses it.
Public extension API
When changing things that extensions might depend on, keep the following stable:
- The
Scriptclass signature (process,before_process,postprocess,postprocess_image, etc.). - The set of named callbacks in
modules/script_callbacks.py. New ones can be added; existing ones must keep their signatures. StableDiffusionProcessingfield names. Renames need a deprecation alias.- The
<lora:name:weight>and<hypernet:name:weight>extra-network syntaxes. - The
/sdapi/v1/*route paths and request/response shapes.
The extension community is large and reactive; breaking changes here usually result in user-visible regressions immediately after merge.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.
Previous
Debugging
Next
Tooling