From f6fc7916c4615c4f5cac97cab5add9b0536f6efa Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Wed, 17 May 2023 22:43:24 +0300 Subject: [PATCH] add /sdapi/v1/script-info api --- modules/api/api.py | 13 +++++++++++-- modules/api/models.py | 21 +++++++++++++++++++-- modules/scripts.py | 29 ++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 165985c3..eee99bbb 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -204,6 +204,7 @@ class Api: self.add_api_route("/sdapi/v1/unload-checkpoint", self.unloadapi, methods=["POST"]) self.add_api_route("/sdapi/v1/reload-checkpoint", self.reloadapi, methods=["POST"]) self.add_api_route("/sdapi/v1/scripts", self.get_scripts_list, methods=["GET"], response_model=models.ScriptsList) + self.add_api_route("/sdapi/v1/script-info", self.get_script_info, methods=["GET"], response_model=List[models.ScriptInfo]) self.default_script_arg_txt2img = [] self.default_script_arg_img2img = [] @@ -229,11 +230,19 @@ class Api: return script, script_idx def get_scripts_list(self): - t2ilist = [str(title.lower()) for title in scripts.scripts_txt2img.titles] - i2ilist = [str(title.lower()) for title in scripts.scripts_img2img.titles] + t2ilist = [script.name for script in scripts.scripts_txt2img.scripts if script.name is not None] + i2ilist = [script.name for script in scripts.scripts_img2img.scripts if script.name is not None] return models.ScriptsList(txt2img=t2ilist, img2img=i2ilist) + def get_script_info(self): + res = [] + + for script_list in [scripts.scripts_txt2img.scripts, scripts.scripts_img2img.scripts]: + res += [script.api_info for script in script_list if script.api_info is not None] + + return res + def get_script(self, script_name, script_runner): if script_name is None or script_name == "": return None, None diff --git a/modules/api/models.py b/modules/api/models.py index 006ccdb7..1ff2fb33 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -287,6 +287,23 @@ class MemoryResponse(BaseModel): ram: dict = Field(title="RAM", description="System memory stats") cuda: dict = Field(title="CUDA", description="nVidia CUDA memory stats") + class ScriptsList(BaseModel): - txt2img: list = Field(default=None,title="Txt2img", description="Titles of scripts (txt2img)") - img2img: list = Field(default=None,title="Img2img", description="Titles of scripts (img2img)") + txt2img: list = Field(default=None, title="Txt2img", description="Titles of scripts (txt2img)") + img2img: list = Field(default=None, title="Img2img", description="Titles of scripts (img2img)") + + +class ScriptArg(BaseModel): + label: str = Field(default=None, title="Label", description="Name of the argument in UI") + value: Optional[Any] = Field(default=None, title="Value", description="Default value of the argument") + minimum: Optional[Any] = Field(default=None, title="Minimum", description="Minimum allowed value for the argumentin UI") + maximum: Optional[Any] = Field(default=None, title="Minimum", description="Maximum allowed value for the argumentin UI") + step: Optional[Any] = Field(default=None, title="Minimum", description="Step for changing value of the argumentin UI") + choices: Optional[List[str]] = Field(default=None, title="Choices", description="Possible values for the argument") + + +class ScriptInfo(BaseModel): + name: str = Field(default=None, title="Name", description="Script name") + is_alwayson: bool = Field(default=None, title="IsAlwayson", description="Flag specifying whether this script is an alwayson script") + is_img2img: bool = Field(default=None, title="IsImg2img", description="Flag specifying whether this script is an img2img script") + args: List[ScriptArg] = Field(title="Arguments", description="List of script's arguments") diff --git a/modules/scripts.py b/modules/scripts.py index 0c12ebd5..e33d8c81 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -17,6 +17,9 @@ class PostprocessImageArgs: class Script: + name = None + """script's internal name derived from title""" + filename = None args_from = None args_to = None @@ -25,8 +28,8 @@ class Script: is_txt2img = False is_img2img = False - """A gr.Group component that has all script's UI inside it""" group = None + """A gr.Group component that has all script's UI inside it""" infotext_fields = None """if set in ui(), this is a list of pairs of gradio component + text; the text will be used when @@ -38,6 +41,9 @@ class Script: various "Send to " buttons when clicked """ + api_info = None + """Generated value of type modules.api.models.ScriptInfo with information about the script for API""" + def title(self): """this function should return the title of the script. This is what will be displayed in the dropdown menu.""" @@ -313,6 +319,8 @@ class ScriptRunner: self.selectable_scripts.append(script) def setup_ui(self): + import modules.api.models as api_models + self.titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in self.selectable_scripts] inputs = [None] @@ -327,9 +335,28 @@ class ScriptRunner: if controls is None: return + script.name = wrap_call(script.title, script.filename, "title", default=script.filename).lower() + api_args = [] + for control in controls: control.custom_script_source = os.path.basename(script.filename) + arg_info = api_models.ScriptArg(label=control.label or "") + + for field in ("value", "minimum", "maximum", "step", "choices"): + v = getattr(control, field, None) + if v is not None: + setattr(arg_info, field, v) + + api_args.append(arg_info) + + script.api_info = api_models.ScriptInfo( + name=script.name, + is_img2img=script.is_img2img, + is_alwayson=script.alwayson, + args=api_args, + ) + if script.infotext_fields is not None: self.infotext_fields += script.infotext_fields