Package: sdwss
sdwss: Stable Diffusion Webui Spaghetti ShieldExample plugin that appends", gloomy atmosphere"to any positive prompt.importsdwssplugin=sdwss.register_plugin("my-plugin")@plugin.wrapperdefencode_prompt_schedule(params:sdwss.EncodePromptScheduleParams,original_function):ifparams.is_positive:ifparams.prompt.strip():params.prompt+=", "params.prompt+="gloomy atmosphere"returnoriginal_function(params)sdwss is a library that exposes many different components of theStable Diffusion Webuithrough a stable programming interface.
It takes care of much of the boilerplate so that extension developers can focus on writing useful code.MotivationDeveloping extensions for the webui is currently very error-prone.
Monkey patching is commonplace, extensions break all the time shortly after a release.
There are many extension repositories that are stale because they are updated less often than the webui.sdwss intends to help stabilize the interface extension developers program against so that things stop breaking all the time.DocumentationThis guide provides basic documentation for plugins intercommunication.TL;DR:Create a plugin usingplugin = sdwss.create_plugin("plugin_name", "0.0.1")Define a function and register it to a plugin using the@plugin.functiondecorator.Use the@plugin.wrapperdecorator to register a function wrapper. You can use the optionalafter=parameter to specify execution order.Create a pluginAny change in the plugin system (registering a new function, wrapping a function, etc.) must be done through a plugin.Here's how to declare a plugin:importsdwssplugin=sdwss.register_plugin("my_awesome_plugin",api_version="0.0.1")A plugin identifier is a series of underscores or lowercase letters ([a-z_]+).
The identifier of a plugin must be unique; two plugins cannot have the same identifier at the same time.The version of a plugin is used to make sure the programming interface the code is written against stays consistent over time.Defining a FunctionA function in sdwss is a regular Python function that can be registered to a plugin.
It performs a specific task and can be extended using wrappers.Example:@plugin.functiondefeat(food):returnf"I eat{food}!"Using thewrapperDecoratorThewrapperdecorator is used to extend or modify the behavior of a registered function.
The wrapper function must have the same name as the target function, unless thenameparameter is used.Without Parameters:@plugin.wrapperdefeat(food,original_function):returnoriginal_function(food).upper()In this example,eatis a wrapper that converts the eating message to uppercase, extending the originaleatfunction defined earlier.With Parameters:@plugin.wrapper(name="eat",after="another_plugin")defcustom_eating(food,original_function):returnf"{original_function(food)}You should as well!"In this example,custom_eatingadds an additional message to the original one.
The parameters of the annotation tells the plugin system to:wrap theeatfunction defined earlier instead of targeting an unknown entity named"custom_eating"only wrap after any wraps provided byanother_pluginApplying WrappersWrappers are applied in the order they are defined, unless specified otherwise using theafterparameter.Using Generator Functions as WrappersGenerator functions can also be used for wrappers.
In a generator wrapper, eachyieldstatement calls the original function and receives its result.Here’s how you can convert a regular function wrapper to a generator wrapper:Regular Function Wrapper:@plugin.wrapperdefeat(food,original_function):returnoriginal_function(food).upper()Generator Function Wrapper:@plugin.wrapperdefeat(food):result=yieldfoodreturnresult.upper()In this generator wrapper example, theeatfunction yields thefoodto the original function and then receives the original function’s result.
The wrapper then proceeds to modify the result by converting it to uppercase before returning it.You can also use multipleyieldstatements in a generator wrapper, which allows for more intricate interactions with the original function.
Eachyieldresults in a call to the original function, and the generator receives the result of that call.Example with Multiple Yields:@plugin.wrapperdefeat(food):result1=yieldfoodresult2=yieldf"{result1}and it's delicious"returnresult2In this example, theeatfunction calls the original function twice, first with the originalfoodargument, and then with an additional question.
The final result is derived from the second call to the original function.Best PracticesMake sure the parameter types of your wrappers match the original function definition.Use descriptive names for your functions and wrappers.When applicable, ensure that your wrapper does not accidentally override or disrupt the behavior of the original function.ContributingContributions are welcome.