models.py 15.8 KB
Newer Older
1
import inspect
2

A
arcticfaded 已提交
3
from pydantic import BaseModel, Field, create_model
A
a666 已提交
4
from typing import Any, Optional, Literal
5
from inflection import underscore
6
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img
B
Bruno Seoane 已提交
7
from modules.shared import sd_upscalers, opts, parser
A
arcticfaded 已提交
8

A
arcticfaded 已提交
9 10 11 12 13 14 15
API_NOT_ALLOWED = [
    "self",
    "kwargs",
    "sd_model",
    "outpath_samples",
    "outpath_grids",
    "sampler_index",
V
Vladimir Mandic 已提交
16 17
    # "do_not_save_samples",
    # "do_not_save_grid",
A
arcticfaded 已提交
18 19 20 21 22 23 24 25 26
    "extra_generation_params",
    "overlay_images",
    "do_not_reload_embeddings",
    "seed_enable_extras",
    "prompt_for_display",
    "sampler_noise_scheduler_override",
    "ddim_discretize"
]

A
arcticfaded 已提交
27 28 29 30 31 32 33
class ModelDef(BaseModel):
    """Assistance Class for Pydantic Dynamic Model Generation"""

    field: str
    field_alias: str
    field_type: Any
    field_value: Any
34
    field_exclude: bool = False
A
arcticfaded 已提交
35 36


A
arcticfaded 已提交
37
class PydanticModelGenerator:
A
arcticfaded 已提交
38
    """
J
Jonathan 已提交
39 40 41
    Takes in created classes and stubs them out in a way FastAPI/Pydantic is happy about:
    source_data is a snapshot of the default values produced by the class
    params are the names of the actual keys required by __init__
A
arcticfaded 已提交
42 43 44 45 46
    """

    def __init__(
        self,
        model_name: str = None,
A
arcticfaded 已提交
47 48
        class_instance = None,
        additional_fields = None,
A
arcticfaded 已提交
49
    ):
A
arcticfaded 已提交
50 51
        def field_type_generator(k, v):
            field_type = v.annotation
52

A
AUTOMATIC1111 已提交
53 54 55 56
            if field_type == 'Image':
                # images are sent as base64 strings via API
                field_type = 'str'

A
arcticfaded 已提交
57
            return Optional[field_type]
58

A
arcticfaded 已提交
59 60 61 62 63 64
        def merge_class_params(class_):
            all_classes = list(filter(lambda x: x is not object, inspect.getmro(class_)))
            parameters = {}
            for classes in all_classes:
                parameters = {**parameters, **inspect.signature(classes.__init__).parameters}
            return parameters
65

A
arcticfaded 已提交
66
        self._model_name = model_name
R
Roy Shilkrot 已提交
67
        self._class_data = merge_class_params(class_instance)
R
Roy Shilkrot 已提交
68

A
arcticfaded 已提交
69 70 71 72
        self._model_def = [
            ModelDef(
                field=underscore(k),
                field_alias=k,
A
arcticfaded 已提交
73
                field_type=field_type_generator(k, v),
A
AUTOMATIC1111 已提交
74
                field_value=None if isinstance(v.default, property) else v.default
A
arcticfaded 已提交
75
            )
A
arcticfaded 已提交
76
            for (k,v) in self._class_data.items() if k not in API_NOT_ALLOWED
A
arcticfaded 已提交
77
        ]
78

A
arcticfaded 已提交
79 80
        for fields in additional_fields:
            self._model_def.append(ModelDef(
81 82
                field=underscore(fields["key"]),
                field_alias=fields["key"],
A
arcticfaded 已提交
83
                field_type=fields["type"],
84 85
                field_value=fields["default"],
                field_exclude=fields["exclude"] if "exclude" in fields else False))
A
arcticfaded 已提交
86 87 88 89 90 91 92

    def generate_model(self):
        """
        Creates a pydantic BaseModel
        from the json and overrides provided at initialization
        """
        fields = {
93
            d.field: (d.field_type, Field(default=d.field_value, alias=d.field_alias, exclude=d.field_exclude)) for d in self._model_def
A
arcticfaded 已提交
94 95
        }
        DynamicModel = create_model(self._model_name, **fields)
W
w-e-w 已提交
96 97
        DynamicModel.model_config['populate_by_name'] = True
        DynamicModel.model_config['frozen'] = True
A
arcticfaded 已提交
98
        return DynamicModel
99

100
StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator(
101
    "StableDiffusionProcessingTxt2Img",
A
arcticfaded 已提交
102
    StableDiffusionProcessingTxt2Img,
103 104 105 106
    [
        {"key": "sampler_index", "type": str, "default": "Euler"},
        {"key": "script_name", "type": str, "default": None},
        {"key": "script_args", "type": list, "default": []},
107 108
        {"key": "send_images", "type": bool, "default": True},
        {"key": "save_images", "type": bool, "default": False},
109
        {"key": "alwayson_scripts", "type": dict, "default": {}},
110
    ]
111 112 113
).generate_model()

StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator(
114
    "StableDiffusionProcessingImg2Img",
115
    StableDiffusionProcessingImg2Img,
116 117 118 119 120 121 122 123
    [
        {"key": "sampler_index", "type": str, "default": "Euler"},
        {"key": "init_images", "type": list, "default": None},
        {"key": "denoising_strength", "type": float, "default": 0.75},
        {"key": "mask", "type": str, "default": None},
        {"key": "include_init_images", "type": bool, "default": False, "exclude" : True},
        {"key": "script_name", "type": str, "default": None},
        {"key": "script_args", "type": list, "default": []},
124 125
        {"key": "send_images", "type": bool, "default": True},
        {"key": "save_images", "type": bool, "default": False},
126
        {"key": "alwayson_scripts", "type": dict, "default": {}},
127
    ]
R
Roy Shilkrot 已提交
128 129
).generate_model()

130
class TextToImageResponse(BaseModel):
A
a666 已提交
131
    images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.")
132 133 134 135
    parameters: dict
    info: str

class ImageToImageResponse(BaseModel):
A
a666 已提交
136
    images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.")
137
    parameters: dict
138
    info: str
139

B
Bruno Seoane 已提交
140 141 142 143 144 145
class ExtrasBaseRequest(BaseModel):
    resize_mode: Literal[0, 1] = Field(default=0, title="Resize Mode", description="Sets the resize mode: 0 to upscale by upscaling_resize amount, 1 to upscale up to upscaling_resize_h x upscaling_resize_w.")
    show_extras_results: bool = Field(default=True, title="Show results", description="Should the backend return the generated image?")
    gfpgan_visibility: float = Field(default=0, title="GFPGAN Visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of GFPGAN, values should be between 0 and 1.")
    codeformer_visibility: float = Field(default=0, title="CodeFormer Visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of CodeFormer, values should be between 0 and 1.")
    codeformer_weight: float = Field(default=0, title="CodeFormer Weight", ge=0, le=1, allow_inf_nan=False, description="Sets the weight of CodeFormer, values should be between 0 and 1.")
146
    upscaling_resize: float = Field(default=2, title="Upscaling Factor", ge=1, le=8, description="By how much to upscale the image, only used when resize_mode=0.")
B
Bruno Seoane 已提交
147 148
    upscaling_resize_w: int = Field(default=512, title="Target Width", ge=1, description="Target width for the upscaler to hit. Only used when resize_mode=1.")
    upscaling_resize_h: int = Field(default=512, title="Target Height", ge=1, description="Target height for the upscaler to hit. Only used when resize_mode=1.")
J
Jim Hays 已提交
149
    upscaling_crop: bool = Field(default=True, title="Crop to fit", description="Should the upscaler crop the image to fit in the chosen size?")
B
Bruno Seoane 已提交
150 151 152
    upscaler_1: str = Field(default="None", title="Main upscaler", description=f"The name of the main upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}")
    upscaler_2: str = Field(default="None", title="Secondary upscaler", description=f"The name of the secondary upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}")
    extras_upscaler_2_visibility: float = Field(default=0, title="Secondary upscaler visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of secondary upscaler, values should be between 0 and 1.")
153
    upscale_first: bool = Field(default=False, title="Upscale first", description="Should the upscaler run before restoring faces?")
B
Bruno Seoane 已提交
154 155

class ExtraBaseResponse(BaseModel):
B
Bruno Seoane 已提交
156
    html_info: str = Field(title="HTML info", description="A series of HTML tags containing the process info.")
B
Bruno Seoane 已提交
157 158 159 160 161

class ExtrasSingleImageRequest(ExtrasBaseRequest):
    image: str = Field(default="", title="Image", description="Image to work on, must be a Base64 string containing the image's data.")

class ExtrasSingleImageResponse(ExtraBaseResponse):
162 163
    image: str = Field(default=None, title="Image", description="The generated image in base64 format.")

B
Bruno Seoane 已提交
164 165 166
class FileData(BaseModel):
    data: str = Field(title="File data", description="Base64 representation of the file")
    name: str = Field(title="File name")
167 168

class ExtrasBatchImagesRequest(ExtrasBaseRequest):
A
a666 已提交
169
    imageList: list[FileData] = Field(title="Images", description="List of images to work on. Must be Base64 strings")
170 171

class ExtrasBatchImagesResponse(ExtraBaseResponse):
A
a666 已提交
172
    images: list[str] = Field(title="Images", description="The generated images in base64 format.")
173

B
Bruno Seoane 已提交
174 175 176 177
class PNGInfoRequest(BaseModel):
    image: str = Field(title="Image", description="The base64 encoded PNG image")

class PNGInfoResponse(BaseModel):
178
    info: str = Field(title="Image info", description="A string with the parameters used to generate the image")
179 180
    items: dict = Field(title="Items", description="A dictionary containing all the other fields the image had")
    parameters: dict = Field(title="Parameters", description="A dictionary with parsed generation info fields")
181

182 183 184
class ProgressRequest(BaseModel):
    skip_current_image: bool = Field(default=False, title="Skip current image", description="Skip current image serialization")

185
class ProgressResponse(BaseModel):
E
evshiron 已提交
186 187
    progress: float = Field(title="Progress", description="The progress with a range of 0 to 1")
    eta_relative: float = Field(title="ETA in secs")
E
evshiron 已提交
188
    state: dict = Field(title="State", description="The current state snapshot")
189
    current_image: str = Field(default=None, title="Current image", description="The current image in base64 format. opts.show_progress_every_n_steps is required for this to work.")
190
    textinfo: str = Field(default=None, title="Info text", description="Info text used by WebUI.")
191 192 193

class InterrogateRequest(BaseModel):
    image: str = Field(default="", title="Image", description="Image to work on, must be a Base64 string containing the image's data.")
194
    model: str = Field(default="clip", title="Model", description="The interrogate model used.")
195 196 197

class InterrogateResponse(BaseModel):
    caption: str = Field(default=None, title="Caption", description="The generated caption for the image.")
198

V
Vladimir Mandic 已提交
199 200 201 202 203 204 205 206 207
class TrainResponse(BaseModel):
    info: str = Field(title="Train info", description="Response string from train embedding or hypernetwork task.")

class CreateResponse(BaseModel):
    info: str = Field(title="Create info", description="Response string from create embedding or hypernetwork task.")

class PreprocessResponse(BaseModel):
    info: str = Field(title="Preprocess info", description="Response string from preprocessing task.")

B
Bruno Seoane 已提交
208
fields = {}
209 210
for key, metadata in opts.data_labels.items():
    value = opts.data.get(key)
W
w-e-w 已提交
211
    optType = opts.typemap.get(type(metadata.default), type(metadata.default)) if metadata.default else Any
B
Bruno Seoane 已提交
212

W
w-e-w 已提交
213
    if metadata is not None:
214
        fields.update({key: (Optional[optType], Field(default=metadata.default, description=metadata.label))})
B
Bruno Seoane 已提交
215 216 217 218 219 220 221 222 223 224
    else:
        fields.update({key: (Optional[optType], Field())})

OptionsModel = create_model("Options", **fields)

flags = {}
_options = vars(parser)['_option_string_actions']
for key in _options:
    if(_options[key].dest != 'help'):
        flag = _options[key]
225
        _type = str
A
AUTOMATIC 已提交
226 227 228
        if _options[key].default is not None:
            _type = type(_options[key].default)
        flags.update({flag.dest: (_type, Field(default=flag.default, description=flag.help))})
B
Bruno Seoane 已提交
229 230 231 232 233

FlagsModel = create_model("Flags", **flags)

class SamplerItem(BaseModel):
    name: str = Field(title="Name")
A
a666 已提交
234 235
    aliases: list[str] = Field(title="Aliases")
    options: dict[str, str] = Field(title="Options")
B
Bruno Seoane 已提交
236 237 238

class UpscalerItem(BaseModel):
    name: str = Field(title="Name")
239 240
    model_name: Optional[str] = Field(title="Model Name")
    model_path: Optional[str] = Field(title="Path")
241
    model_url: Optional[str] = Field(title="URL")
242
    scale: Optional[float] = Field(title="Scale")
B
Bruno Seoane 已提交
243

244 245 246
class LatentUpscalerModeItem(BaseModel):
    name: str = Field(title="Name")

B
Bruno Seoane 已提交
247 248 249
class SDModelItem(BaseModel):
    title: str = Field(title="Title")
    model_name: str = Field(title="Model Name")
A
AUTOMATIC 已提交
250 251
    hash: Optional[str] = Field(title="Short hash")
    sha256: Optional[str] = Field(title="sha256 hash")
B
Bruno Seoane 已提交
252
    filename: str = Field(title="Filename")
A
AUTOMATIC 已提交
253
    config: Optional[str] = Field(title="Config file")
B
Bruno Seoane 已提交
254

J
James 已提交
255 256 257 258
class SDVaeItem(BaseModel):
    model_name: str = Field(title="Model Name")
    filename: str = Field(title="Filename")

B
Bruno Seoane 已提交
259 260
class HypernetworkItem(BaseModel):
    name: str = Field(title="Name")
261
    path: Optional[str] = Field(title="Path")
B
Bruno Seoane 已提交
262 263 264

class FaceRestorerItem(BaseModel):
    name: str = Field(title="Name")
265
    cmd_dir: Optional[str] = Field(title="Path")
B
Bruno Seoane 已提交
266 267 268

class RealesrganItem(BaseModel):
    name: str = Field(title="Name")
269 270
    path: Optional[str] = Field(title="Path")
    scale: Optional[int] = Field(title="Scale")
B
Bruno Seoane 已提交
271 272 273

class PromptStyleItem(BaseModel):
    name: str = Field(title="Name")
274 275
    prompt: Optional[str] = Field(title="Prompt")
    negative_prompt: Optional[str] = Field(title="Negative Prompt")
B
Bruno Seoane 已提交
276

277

278 279 280 281 282 283 284
class EmbeddingItem(BaseModel):
    step: Optional[int] = Field(title="Step", description="The number of steps that were used to train this embedding, if available")
    sd_checkpoint: Optional[str] = Field(title="SD Checkpoint", description="The hash of the checkpoint this embedding was trained on, if available")
    sd_checkpoint_name: Optional[str] = Field(title="SD Checkpoint Name", description="The name of the checkpoint this embedding was trained on, if available. Note that this is the name that was used by the trainer; for a stable identifier, use `sd_checkpoint` instead")
    shape: int = Field(title="Shape", description="The length of each individual vector in the embedding")
    vectors: int = Field(title="Vectors", description="The number of vectors in the embedding")

P
Philpax 已提交
285
class EmbeddingsResponse(BaseModel):
A
a666 已提交
286 287
    loaded: dict[str, EmbeddingItem] = Field(title="Loaded", description="Embeddings loaded for the current model")
    skipped: dict[str, EmbeddingItem] = Field(title="Skipped", description="Embeddings skipped for the current model (likely due to architecture incompatibility)")
V
Vladimir Mandic 已提交
288 289

class MemoryResponse(BaseModel):
290 291
    ram: dict = Field(title="RAM", description="System memory stats")
    cuda: dict = Field(title="CUDA", description="nVidia CUDA memory stats")
Y
Yea chen 已提交
292

A
AUTOMATIC 已提交
293

Y
Yea chen 已提交
294
class ScriptsList(BaseModel):
A
AUTOMATIC 已提交
295 296 297 298 299 300 301 302 303 304
    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")
A
a666 已提交
305
    choices: Optional[list[str]] = Field(default=None, title="Choices", description="Possible values for the argument")
A
AUTOMATIC 已提交
306 307 308 309 310 311


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")
A
a666 已提交
312
    args: list[ScriptArg] = Field(title="Arguments", description="List of script's arguments")
313 314 315 316 317 318 319 320 321

class ExtensionItem(BaseModel):
    name: str = Field(title="Name", description="Extension name")
    remote: str = Field(title="Remote", description="Extension Repository URL")
    branch: str = Field(title="Branch", description="Extension Repository Branch")
    commit_hash: str = Field(title="Commit Hash", description="Extension Repository Commit Hash")
    version: str = Field(title="Version", description="Extension Version")
    commit_date: str = Field(title="Commit Date", description="Extension Repository Commit Date")
    enabled: bool = Field(title="Enabled", description="Flag specifying whether this extension is enabled")