models.py 7.6 KB
Newer Older
1
import inspect
B
Bruno Seoane 已提交
2
from click import prompt
A
arcticfaded 已提交
3
from pydantic import BaseModel, Field, create_model
4
from typing import Any, Optional
B
Bruno Seoane 已提交
5
from typing_extensions import Literal
6
from inflection import underscore
7
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img
B
Bruno Seoane 已提交
8
from modules.shared import sd_upscalers
A
arcticfaded 已提交
9

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

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

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


A
arcticfaded 已提交
38
class PydanticModelGenerator:
A
arcticfaded 已提交
39
    """
J
Jonathan 已提交
40 41 42
    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 已提交
43 44 45 46 47
    """

    def __init__(
        self,
        model_name: str = None,
A
arcticfaded 已提交
48 49
        class_instance = None,
        additional_fields = None,
A
arcticfaded 已提交
50
    ):
A
arcticfaded 已提交
51 52 53 54
        def field_type_generator(k, v):
            # field_type = str if not overrides.get(k) else overrides[k]["type"]
            # print(k, v.annotation, v.default)
            field_type = v.annotation
55

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

A
arcticfaded 已提交
58 59 60 61 62 63
        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
64 65


A
arcticfaded 已提交
66
        self._model_name = model_name
A
arcticfaded 已提交
67
        self._class_data = merge_class_params(class_instance)
A
arcticfaded 已提交
68 69 70 71
        self._model_def = [
            ModelDef(
                field=underscore(k),
                field_alias=k,
A
arcticfaded 已提交
72
                field_type=field_type_generator(k, v),
S
clean  
Stephen 已提交
73
                field_value=v.default
A
arcticfaded 已提交
74
            )
A
arcticfaded 已提交
75
            for (k,v) in self._class_data.items() if k not in API_NOT_ALLOWED
A
arcticfaded 已提交
76
        ]
77

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

    def generate_model(self):
        """
        Creates a pydantic BaseModel
        from the json and overrides provided at initialization
        """
        fields = {
92
            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 已提交
93 94 95
        }
        DynamicModel = create_model(self._model_name, **fields)
        DynamicModel.__config__.allow_population_by_field_name = True
A
arcticfaded 已提交
96
        DynamicModel.__config__.allow_mutation = True
A
arcticfaded 已提交
97
        return DynamicModel
98

99
StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator(
100
    "StableDiffusionProcessingTxt2Img",
A
arcticfaded 已提交
101
    StableDiffusionProcessingTxt2Img,
A
arcticfaded 已提交
102
    [{"key": "sampler_index", "type": str, "default": "Euler"}]
103 104 105
).generate_model()

StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator(
106
    "StableDiffusionProcessingImg2Img",
107
    StableDiffusionProcessingImg2Img,
108
    [{"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}]
109 110
).generate_model()

111 112
class TextToImageResponse(BaseModel):
    images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.")
113 114 115 116 117 118
    parameters: dict
    info: str

class ImageToImageResponse(BaseModel):
    images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.")
    parameters: dict
119
    info: str
120

B
Bruno Seoane 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
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.")
    upscaling_resize: float = Field(default=2, title="Upscaling Factor", ge=1, le=4, description="By how much to upscale the image, only used when resize_mode=0.")
    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.")
    upscaling_crop: bool = Field(default=True, title="Crop to fit", description="Should the upscaler crop the image to fit in the choosen size?")
    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.")

class ExtraBaseResponse(BaseModel):
B
Bruno Seoane 已提交
136
    html_info: str = Field(title="HTML info", description="A series of HTML tags containing the process info.")
B
Bruno Seoane 已提交
137 138 139 140 141

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):
142 143
    image: str = Field(default=None, title="Image", description="The generated image in base64 format.")

B
Bruno Seoane 已提交
144 145 146
class FileData(BaseModel):
    data: str = Field(title="File data", description="Base64 representation of the file")
    name: str = Field(title="File name")
147 148

class ExtrasBatchImagesRequest(ExtrasBaseRequest):
B
Bruno Seoane 已提交
149
    imageList: list[FileData] = Field(title="Images", description="List of images to work on. Must be Base64 strings")
150 151

class ExtrasBatchImagesResponse(ExtraBaseResponse):
152 153
    images: list[str] = Field(title="Images", description="The generated images in base64 format.")

B
Bruno Seoane 已提交
154 155 156 157
class PNGInfoRequest(BaseModel):
    image: str = Field(title="Image", description="The base64 encoded PNG image")

class PNGInfoResponse(BaseModel):
158 159
    info: str = Field(title="Image info", description="A string with all the info the image had")

160 161 162 163
class ProgressResponse(BaseModel):
    progress: float
    eta_relative: float
    state: dict