Unverified Commit e9c767d8 authored by AUTOMATIC1111's avatar AUTOMATIC1111 Committed by GitHub

Merge branch 'master' into 7flash/fix-api-compatibility

parents b2c48091 2913b9f0
...@@ -155,14 +155,15 @@ The documentation was moved from this README over to the project's [wiki](https: ...@@ -155,14 +155,15 @@ The documentation was moved from this README over to the project's [wiki](https:
- Swin2SR - https://github.com/mv-lab/swin2sr - Swin2SR - https://github.com/mv-lab/swin2sr
- LDSR - https://github.com/Hafiidz/latent-diffusion - LDSR - https://github.com/Hafiidz/latent-diffusion
- Ideas for optimizations - https://github.com/basujindal/stable-diffusion - Ideas for optimizations - https://github.com/basujindal/stable-diffusion
- Doggettx - Cross Attention layer optimization - https://github.com/Doggettx/stable-diffusion, original idea for prompt editing. - Cross Attention layer optimization - Doggettx - https://github.com/Doggettx/stable-diffusion, original idea for prompt editing.
- InvokeAI, lstein - Cross Attention layer optimization - https://github.com/invoke-ai/InvokeAI (originally http://github.com/lstein/stable-diffusion) - Cross Attention layer optimization - InvokeAI, lstein - https://github.com/invoke-ai/InvokeAI (originally http://github.com/lstein/stable-diffusion)
- Rinon Gal - Textual Inversion - https://github.com/rinongal/textual_inversion (we're not using his code, but we are using his ideas). - Textual Inversion - Rinon Gal - https://github.com/rinongal/textual_inversion (we're not using his code, but we are using his ideas).
- Idea for SD upscale - https://github.com/jquesnelle/txt2imghd - Idea for SD upscale - https://github.com/jquesnelle/txt2imghd
- Noise generation for outpainting mk2 - https://github.com/parlance-zz/g-diffuser-bot - Noise generation for outpainting mk2 - https://github.com/parlance-zz/g-diffuser-bot
- CLIP interrogator idea and borrowing some code - https://github.com/pharmapsychotic/clip-interrogator - CLIP interrogator idea and borrowing some code - https://github.com/pharmapsychotic/clip-interrogator
- Idea for Composable Diffusion - https://github.com/energy-based-model/Compositional-Visual-Generation-with-Composable-Diffusion-Models-PyTorch - Idea for Composable Diffusion - https://github.com/energy-based-model/Compositional-Visual-Generation-with-Composable-Diffusion-Models-PyTorch
- xformers - https://github.com/facebookresearch/xformers - xformers - https://github.com/facebookresearch/xformers
- DeepDanbooru - interrogator for anime diffusers https://github.com/KichangKim/DeepDanbooru - DeepDanbooru - interrogator for anime diffusers https://github.com/KichangKim/DeepDanbooru
- Security advice - RyotaK
- Initial Gradio script - posted on 4chan by an Anonymous user. Thank you Anonymous user. - Initial Gradio script - posted on 4chan by an Anonymous user. Thank you Anonymous user.
- (You) - (You)
addEventListener('keydown', (event) => { addEventListener('keydown', (event) => {
let target = event.originalTarget || event.composedPath()[0]; let target = event.originalTarget || event.composedPath()[0];
if (!target.hasAttribute("placeholder")) return; if (!target.matches("#toprow textarea.gr-text-input[placeholder]")) return;
if (!target.placeholder.toLowerCase().includes("prompt")) return;
if (! (event.metaKey || event.ctrlKey)) return; if (! (event.metaKey || event.ctrlKey)) return;
......
...@@ -104,6 +104,7 @@ ...@@ -104,6 +104,7 @@
"Seed travel": "Interpolazione semi", "Seed travel": "Interpolazione semi",
"Shift attention": "Sposta l'attenzione", "Shift attention": "Sposta l'attenzione",
"Text to Vector Graphics": "Da testo a grafica vettoriale", "Text to Vector Graphics": "Da testo a grafica vettoriale",
"Unprompted": "Unprompted",
"X/Y plot": "Grafico X/Y", "X/Y plot": "Grafico X/Y",
"X/Y/Z plot": "Grafico X/Y/Z", "X/Y/Z plot": "Grafico X/Y/Z",
"Dynamic Prompting v0.13.6": "Prompt dinamici v0.13.6", "Dynamic Prompting v0.13.6": "Prompt dinamici v0.13.6",
...@@ -259,6 +260,7 @@ ...@@ -259,6 +260,7 @@
"Save results as video": "Salva i risultati come video", "Save results as video": "Salva i risultati come video",
"Frames per second": "Fotogrammi al secondo", "Frames per second": "Fotogrammi al secondo",
"Iterate seed every line": "Iterare il seme per ogni riga", "Iterate seed every line": "Iterare il seme per ogni riga",
"Use same random seed for all lines": "Usa lo stesso seme casuale per tutte le righe",
"List of prompt inputs": "Elenco di prompt di input", "List of prompt inputs": "Elenco di prompt di input",
"Upload prompt inputs": "Carica un file contenente i prompt di input", "Upload prompt inputs": "Carica un file contenente i prompt di input",
"n": "Esegui n volte", "n": "Esegui n volte",
...@@ -294,6 +296,13 @@ ...@@ -294,6 +296,13 @@
"Transparent PNG": "PNG trasparente", "Transparent PNG": "PNG trasparente",
"Noise Tolerance": "Tolleranza al rumore", "Noise Tolerance": "Tolleranza al rumore",
"Quantize": "Quantizzare", "Quantize": "Quantizzare",
"Dry Run": "Esecuzione a vuoto (Debug)",
"NEW!": "NUOVO!",
"Premium Fantasy Card Template": "Premium Fantasy Card Template",
"is now available.": "è ora disponibile.",
"Generate a wide variety of creatures and characters in the style of a fantasy card game. Perfect for heroes, animals, monsters, and even crazy hybrids.": "Genera un'ampia varietà di creature e personaggi nello stile di un gioco di carte fantasy. Perfetto per eroi, animali, mostri e persino ibridi incredibili.",
"Learn More ➜": "Per saperne di più ➜",
"Purchases help fund the continued development of Unprompted. Thank you for your support!": "Gli acquisti aiutano a finanziare il continuo sviluppo di Unprompted. Grazie per il vostro sostegno!",
"X type": "Parametro asse X", "X type": "Parametro asse X",
"Nothing": "Niente", "Nothing": "Niente",
"Var. seed": "Seme della variazione", "Var. seed": "Seme della variazione",
...@@ -424,6 +433,7 @@ ...@@ -424,6 +433,7 @@
"Sigma adjustment for finding noise for image": "Regolazione Sigma per trovare il rumore per l'immagine", "Sigma adjustment for finding noise for image": "Regolazione Sigma per trovare il rumore per l'immagine",
"Tile size": "Dimensione piastrella", "Tile size": "Dimensione piastrella",
"Tile overlap": "Sovrapposizione piastrella", "Tile overlap": "Sovrapposizione piastrella",
"New seed for each tile": "Nuovo seme per ogni piastrella",
"alternate img2img imgage": "Immagine alternativa per img2img", "alternate img2img imgage": "Immagine alternativa per img2img",
"interpolation values": "Valori di interpolazione", "interpolation values": "Valori di interpolazione",
"Refinement loops": "Cicli di affinamento", "Refinement loops": "Cicli di affinamento",
...@@ -455,8 +465,9 @@ ...@@ -455,8 +465,9 @@
"Will upscale the image to twice the dimensions; use width and height sliders to set tile size": "Aumenterà l'immagine al doppio delle dimensioni; utilizzare i cursori di larghezza e altezza per impostare la dimensione della piastrella", "Will upscale the image to twice the dimensions; use width and height sliders to set tile size": "Aumenterà l'immagine al doppio delle dimensioni; utilizzare i cursori di larghezza e altezza per impostare la dimensione della piastrella",
"Upscaler": "Ampliamento immagine", "Upscaler": "Ampliamento immagine",
"Lanczos": "Lanczos", "Lanczos": "Lanczos",
"Nearest": "Nearest",
"LDSR": "LDSR", "LDSR": "LDSR",
"ESRGAN_4x": "ESRGAN_4x", "BSRGAN": "BSRGAN",
"ScuNET GAN": "ScuNET GAN", "ScuNET GAN": "ScuNET GAN",
"ScuNET PSNR": "ScuNET PSNR", "ScuNET PSNR": "ScuNET PSNR",
"SwinIR 4x": "SwinIR 4x", "SwinIR 4x": "SwinIR 4x",
...@@ -808,6 +819,7 @@ ...@@ -808,6 +819,7 @@
"image_path": "Percorso immagine", "image_path": "Percorso immagine",
"mp4_path": "Percorso MP4", "mp4_path": "Percorso MP4",
"Click here after the generation to show the video": "Clicca qui dopo la generazione per mostrare il video", "Click here after the generation to show the video": "Clicca qui dopo la generazione per mostrare il video",
"NOTE: If the 'Generate' button doesn't work, go in Settings and click 'Restart Gradio and Refresh...'.": "NOTA: se il pulsante 'Genera' non funziona, vai in Impostazioni e fai clic su 'Riavvia Gradio e Aggiorna...'.",
"Save Settings": "Salva le impostazioni", "Save Settings": "Salva le impostazioni",
"Load Settings": "Carica le impostazioni", "Load Settings": "Carica le impostazioni",
"Path relative to the webui folder." : "Percorso relativo alla cartella webui.", "Path relative to the webui folder." : "Percorso relativo alla cartella webui.",
...@@ -922,8 +934,8 @@ ...@@ -922,8 +934,8 @@
"Renew Page": "Aggiorna la pagina", "Renew Page": "Aggiorna la pagina",
"Number": "Numero", "Number": "Numero",
"set_index": "Imposta indice", "set_index": "Imposta indice",
"load_switch": "load_switch", "load_switch": "Carica",
"turn_page_switch": "turn_page_switch", "turn_page_switch": "Volta pagina",
"Checkbox": "Casella di controllo", "Checkbox": "Casella di controllo",
"Checkbox Group": "Seleziona immagini per", "Checkbox Group": "Seleziona immagini per",
"artists": "Artisti", "artists": "Artisti",
...@@ -956,6 +968,8 @@ ...@@ -956,6 +968,8 @@
"Save text information about generation parameters as chunks to png files": "Salva le informazioni di testo dei parametri di generazione come blocchi nel file png", "Save text information about generation parameters as chunks to png files": "Salva le informazioni di testo dei parametri di generazione come blocchi nel file png",
"Create a text file next to every image with generation parameters.": "Crea un file di testo assieme a ogni immagine con i parametri di generazione.", "Create a text file next to every image with generation parameters.": "Crea un file di testo assieme a ogni immagine con i parametri di generazione.",
"Save a copy of image before doing face restoration.": "Salva una copia dell'immagine prima di eseguire il restauro dei volti.", "Save a copy of image before doing face restoration.": "Salva una copia dell'immagine prima di eseguire il restauro dei volti.",
"Save a copy of image before applying highres fix.": "Salva una copia dell'immagine prima di applicare la correzione ad alta risoluzione.",
"Save a copy of image before applying color correction to img2img results": "Salva una copia dell'immagine prima di applicare la correzione del colore ai risultati di img2img",
"Quality for saved jpeg images": "Qualità delle immagini salvate in formato JPEG", "Quality for saved jpeg images": "Qualità delle immagini salvate in formato JPEG",
"If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG": "Se l'immagine PNG è più grande di 4 MB o qualsiasi dimensione è maggiore di 4000, ridimensiona e salva la copia come JPG", "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG": "Se l'immagine PNG è più grande di 4 MB o qualsiasi dimensione è maggiore di 4000, ridimensiona e salva la copia come JPG",
"Use original name for output filename during batch process in extras tab": "Usa il nome originale per il nome del file di output durante l'elaborazione a lotti nella scheda 'Extra'", "Use original name for output filename during batch process in extras tab": "Usa il nome originale per il nome del file di output durante l'elaborazione a lotti nella scheda 'Extra'",
...@@ -997,12 +1011,14 @@ ...@@ -997,12 +1011,14 @@
"Filename join string": "Stringa per unire le parole estratte dal nome del file", "Filename join string": "Stringa per unire le parole estratte dal nome del file",
"Number of repeats for a single input image per epoch; used only for displaying epoch number": "Numero di ripetizioni per una singola immagine di input per epoca; utilizzato solo per visualizzare il numero di epoca", "Number of repeats for a single input image per epoch; used only for displaying epoch number": "Numero di ripetizioni per una singola immagine di input per epoca; utilizzato solo per visualizzare il numero di epoca",
"Save an csv containing the loss to log directory every N steps, 0 to disable": "Salva un file CSV contenente la perdita nella cartella di registrazione ogni N passaggi, 0 per disabilitare", "Save an csv containing the loss to log directory every N steps, 0 to disable": "Salva un file CSV contenente la perdita nella cartella di registrazione ogni N passaggi, 0 per disabilitare",
"Use cross attention optimizations while training": "Usa le ottimizzazioni di controllo dell'attenzione incrociato durante l'allenamento",
"Stable Diffusion": "Stable Diffusion", "Stable Diffusion": "Stable Diffusion",
"Checkpoints to cache in RAM": "Checkpoint da memorizzare nella RAM", "Checkpoints to cache in RAM": "Checkpoint da memorizzare nella RAM",
"SD VAE": "SD VAE",
"auto": "auto",
"Hypernetwork strength": "Forza della Iperrete", "Hypernetwork strength": "Forza della Iperrete",
"Inpainting conditioning mask strength": "Forza della maschera di condizionamento del Inpainting", "Inpainting conditioning mask strength": "Forza della maschera di condizionamento del Inpainting",
"Apply color correction to img2img results to match original colors.": "Applica la correzione del colore ai risultati di img2img in modo che corrispondano ai colori originali.", "Apply color correction to img2img results to match original colors.": "Applica la correzione del colore ai risultati di img2img in modo che corrispondano ai colori originali.",
"Save a copy of image before applying color correction to img2img results": "Salva una copia dell'immagine prima di applicare la correzione del colore ai risultati di img2img",
"With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising).": "Con img2img, esegue esattamente la quantità di passi specificata dalla barra di scorrimento (normalmente se ne effettuano di meno con meno riduzione del rumore).", "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising).": "Con img2img, esegue esattamente la quantità di passi specificata dalla barra di scorrimento (normalmente se ne effettuano di meno con meno riduzione del rumore).",
"Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds. Requires restart to apply.": "Abilita la quantizzazione nei campionatori K per risultati più nitidi e puliti. Questo può cambiare i semi esistenti. Richiede il riavvio per applicare la modifica.", "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds. Requires restart to apply.": "Abilita la quantizzazione nei campionatori K per risultati più nitidi e puliti. Questo può cambiare i semi esistenti. Richiede il riavvio per applicare la modifica.",
"Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention": "Enfasi: utilizzare (testo) per fare in modo che il modello presti maggiore attenzione al testo e [testo] per fargli prestare meno attenzione", "Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention": "Enfasi: utilizzare (testo) per fare in modo che il modello presti maggiore attenzione al testo e [testo] per fargli prestare meno attenzione",
...@@ -1194,8 +1210,8 @@ ...@@ -1194,8 +1210,8 @@
"Hue:0": "Hue:0", "Hue:0": "Hue:0",
"S:0": "S:0", "S:0": "S:0",
"L:0": "L:0", "L:0": "L:0",
"Load Canvas": "Carica Tela", "Load Canvas": "Carica Canvas",
"saveCanvas": "Salva Tela", "Save Canvas": "Salva Canvas",
"latest": "aggiornato", "latest": "aggiornato",
"behind": "da aggiornare", "behind": "da aggiornare",
"Description": "Descrizione", "Description": "Descrizione",
......
This diff is collapsed.
This diff is collapsed.
...@@ -2,14 +2,17 @@ import base64 ...@@ -2,14 +2,17 @@ import base64
import io import io
import time import time
import uvicorn import uvicorn
from gradio.processing_utils import decode_base64_to_file, decode_base64_to_image from threading import Lock
from fastapi import APIRouter, Depends, HTTPException from gradio.processing_utils import encode_pil_to_base64, decode_base64_to_file, decode_base64_to_image
from fastapi import APIRouter, Depends, FastAPI, HTTPException
import modules.shared as shared import modules.shared as shared
from modules.api.models import * from modules.api.models import *
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images
from modules.sd_samplers import all_samplers, sample_to_image, samples_to_image_grid from modules.sd_samplers import all_samplers
from modules.extras import run_extras, run_pnginfo from modules.extras import run_extras, run_pnginfo
from modules.sd_models import checkpoints_list
from modules.realesrgan_model import get_realesrgan_models
from typing import List
def upscaler_to_index(name: str): def upscaler_to_index(name: str):
try: try:
...@@ -37,7 +40,7 @@ def encode_pil_to_base64(image): ...@@ -37,7 +40,7 @@ def encode_pil_to_base64(image):
class Api: class Api:
def __init__(self, app, queue_lock): def __init__(self, app: FastAPI, queue_lock: Lock):
self.router = APIRouter() self.router = APIRouter()
self.app = app self.app = app
self.queue_lock = queue_lock self.queue_lock = queue_lock
...@@ -48,6 +51,18 @@ class Api: ...@@ -48,6 +51,18 @@ class Api:
self.app.add_api_route("/sdapi/v1/png-info", self.pnginfoapi, methods=["POST"], response_model=PNGInfoResponse) self.app.add_api_route("/sdapi/v1/png-info", self.pnginfoapi, methods=["POST"], response_model=PNGInfoResponse)
self.app.add_api_route("/sdapi/v1/progress", self.progressapi, methods=["GET"], response_model=ProgressResponse) self.app.add_api_route("/sdapi/v1/progress", self.progressapi, methods=["GET"], response_model=ProgressResponse)
self.app.add_api_route("/sdapi/v1/interrupt", self.interruptapi, methods=["POST"]) self.app.add_api_route("/sdapi/v1/interrupt", self.interruptapi, methods=["POST"])
self.app.add_api_route("/sdapi/v1/options", self.get_config, methods=["GET"], response_model=OptionsModel)
self.app.add_api_route("/sdapi/v1/options", self.set_config, methods=["POST"])
self.app.add_api_route("/sdapi/v1/cmd-flags", self.get_cmd_flags, methods=["GET"], response_model=FlagsModel)
self.app.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=List[SamplerItem])
self.app.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=List[UpscalerItem])
self.app.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=List[SDModelItem])
self.app.add_api_route("/sdapi/v1/hypernetworks", self.get_hypernetworks, methods=["GET"], response_model=List[HypernetworkItem])
self.app.add_api_route("/sdapi/v1/face-restorers", self.get_face_restorers, methods=["GET"], response_model=List[FaceRestorerItem])
self.app.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=List[RealesrganItem])
self.app.add_api_route("/sdapi/v1/prompt-styles", self.get_promp_styles, methods=["GET"], response_model=List[PromptStyleItem])
self.app.add_api_route("/sdapi/v1/artist-categories", self.get_artists_categories, methods=["GET"], response_model=List[str])
self.app.add_api_route("/sdapi/v1/artists", self.get_artists, methods=["GET"], response_model=List[ArtistItem])
def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI):
sampler_index = sampler_to_index(txt2imgreq.sampler_index) sampler_index = sampler_to_index(txt2imgreq.sampler_index)
...@@ -190,6 +205,66 @@ class Api: ...@@ -190,6 +205,66 @@ class Api:
shared.state.interrupt() shared.state.interrupt()
return {} return {}
def get_config(self):
options = {}
for key in shared.opts.data.keys():
metadata = shared.opts.data_labels.get(key)
if(metadata is not None):
options.update({key: shared.opts.data.get(key, shared.opts.data_labels.get(key).default)})
else:
options.update({key: shared.opts.data.get(key, None)})
return options
def set_config(self, req: OptionsModel):
reqDict = vars(req)
for o in reqDict:
setattr(shared.opts, o, reqDict[o])
shared.opts.save(shared.config_filename)
return
def get_cmd_flags(self):
return vars(shared.cmd_opts)
def get_samplers(self):
return [{"name":sampler[0], "aliases":sampler[2], "options":sampler[3]} for sampler in all_samplers]
def get_upscalers(self):
upscalers = []
for upscaler in shared.sd_upscalers:
u = upscaler.scaler
upscalers.append({"name":u.name, "model_name":u.model_name, "model_path":u.model_path, "model_url":u.model_url})
return upscalers
def get_sd_models(self):
return [{"title":x.title, "model_name":x.model_name, "hash":x.hash, "filename": x.filename, "config": x.config} for x in checkpoints_list.values()]
def get_hypernetworks(self):
return [{"name": name, "path": shared.hypernetworks[name]} for name in shared.hypernetworks]
def get_face_restorers(self):
return [{"name":x.name(), "cmd_dir": getattr(x, "cmd_dir", None)} for x in shared.face_restorers]
def get_realesrgan_models(self):
return [{"name":x.name,"path":x.data_path, "scale":x.scale} for x in get_realesrgan_models(None)]
def get_promp_styles(self):
styleList = []
for k in shared.prompt_styles.styles:
style = shared.prompt_styles.styles[k]
styleList.append({"name":style[0], "prompt": style[1], "negative_prompr": style[2]})
return styleList
def get_artists_categories(self):
return shared.artist_db.cats
def get_artists(self):
return [{"name":x[0], "score":x[1], "category":x[2]} for x in shared.artist_db.artists]
def launch(self, server_name, port): def launch(self, server_name, port):
self.app.include_router(self.router) self.app.include_router(self.router)
......
import inspect import inspect
from click import prompt
from pydantic import BaseModel, Field, create_model from pydantic import BaseModel, Field, create_model
from typing import Any, Optional from typing import Any, Optional, Union
from typing_extensions import Literal from typing_extensions import Literal
from inflection import underscore from inflection import underscore
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img
from modules.shared import sd_upscalers from modules.shared import sd_upscalers, opts, parser
from typing import List from typing import List
API_NOT_ALLOWED = [ API_NOT_ALLOWED = [
...@@ -132,6 +131,7 @@ class ExtrasBaseRequest(BaseModel): ...@@ -132,6 +131,7 @@ class ExtrasBaseRequest(BaseModel):
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_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])}") 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.") 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.")
upscale_first: bool = Field(default=False, title="Upscale first", description="Should the upscaler run before restoring faces?")
class ExtraBaseResponse(BaseModel): class ExtraBaseResponse(BaseModel):
html_info: str = Field(title="HTML info", description="A series of HTML tags containing the process info.") html_info: str = Field(title="HTML info", description="A series of HTML tags containing the process info.")
...@@ -166,3 +166,68 @@ class ProgressResponse(BaseModel): ...@@ -166,3 +166,68 @@ class ProgressResponse(BaseModel):
eta_relative: float = Field(title="ETA in secs") eta_relative: float = Field(title="ETA in secs")
state: dict = Field(title="State", description="The current state snapshot") state: dict = Field(title="State", description="The current state snapshot")
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.") 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.")
fields = {}
for key, value in opts.data.items():
metadata = opts.data_labels.get(key)
optType = opts.typemap.get(type(value), type(value))
if (metadata is not None):
fields.update({key: (Optional[optType], Field(
default=metadata.default ,description=metadata.label))})
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]
_type = str
if(_options[key].default != None): _type = type(_options[key].default)
flags.update({flag.dest: (_type,Field(default=flag.default, description=flag.help))})
FlagsModel = create_model("Flags", **flags)
class SamplerItem(BaseModel):
name: str = Field(title="Name")
aliases: list[str] = Field(title="Aliases")
options: dict[str, str] = Field(title="Options")
class UpscalerItem(BaseModel):
name: str = Field(title="Name")
model_name: str | None = Field(title="Model Name")
model_path: str | None = Field(title="Path")
model_url: str | None = Field(title="URL")
class SDModelItem(BaseModel):
title: str = Field(title="Title")
model_name: str = Field(title="Model Name")
hash: str = Field(title="Hash")
filename: str = Field(title="Filename")
config: str = Field(title="Config file")
class HypernetworkItem(BaseModel):
name: str = Field(title="Name")
path: str | None = Field(title="Path")
class FaceRestorerItem(BaseModel):
name: str = Field(title="Name")
cmd_dir: str | None = Field(title="Path")
class RealesrganItem(BaseModel):
name: str = Field(title="Name")
path: str | None = Field(title="Path")
scale: int | None = Field(title="Scale")
class PromptStyleItem(BaseModel):
name: str = Field(title="Name")
prompt: str | None = Field(title="Prompt")
negative_prompt: str | None = Field(title="Negative Prompt")
class ArtistItem(BaseModel):
name: str = Field(title="Name")
score: float = Field(title="Score")
category: str = Field(title="Category")
\ No newline at end of file
...@@ -35,7 +35,8 @@ class HypernetworkModule(torch.nn.Module): ...@@ -35,7 +35,8 @@ class HypernetworkModule(torch.nn.Module):
} }
activation_dict.update({cls_name.lower(): cls_obj for cls_name, cls_obj in inspect.getmembers(torch.nn.modules.activation) if inspect.isclass(cls_obj) and cls_obj.__module__ == 'torch.nn.modules.activation'}) activation_dict.update({cls_name.lower(): cls_obj for cls_name, cls_obj in inspect.getmembers(torch.nn.modules.activation) if inspect.isclass(cls_obj) and cls_obj.__module__ == 'torch.nn.modules.activation'})
def __init__(self, dim, state_dict=None, layer_structure=None, activation_func=None, weight_init='Normal', add_layer_norm=False, use_dropout=False): def __init__(self, dim, state_dict=None, layer_structure=None, activation_func=None, weight_init='Normal',
add_layer_norm=False, use_dropout=False, activate_output=False, last_layer_dropout=True):
super().__init__() super().__init__()
assert layer_structure is not None, "layer_structure must not be None" assert layer_structure is not None, "layer_structure must not be None"
...@@ -48,8 +49,8 @@ class HypernetworkModule(torch.nn.Module): ...@@ -48,8 +49,8 @@ class HypernetworkModule(torch.nn.Module):
# Add a fully-connected layer # Add a fully-connected layer
linears.append(torch.nn.Linear(int(dim * layer_structure[i]), int(dim * layer_structure[i+1]))) linears.append(torch.nn.Linear(int(dim * layer_structure[i]), int(dim * layer_structure[i+1])))
# Add an activation func # Add an activation func except last layer
if activation_func == "linear" or activation_func is None: if activation_func == "linear" or activation_func is None or (i >= len(layer_structure) - 2 and not activate_output):
pass pass
elif activation_func in self.activation_dict: elif activation_func in self.activation_dict:
linears.append(self.activation_dict[activation_func]()) linears.append(self.activation_dict[activation_func]())
...@@ -60,8 +61,8 @@ class HypernetworkModule(torch.nn.Module): ...@@ -60,8 +61,8 @@ class HypernetworkModule(torch.nn.Module):
if add_layer_norm: if add_layer_norm:
linears.append(torch.nn.LayerNorm(int(dim * layer_structure[i+1]))) linears.append(torch.nn.LayerNorm(int(dim * layer_structure[i+1])))
# Add dropout expect last layer # Add dropout except last layer
if use_dropout and i < len(layer_structure) - 3: if use_dropout and (i < len(layer_structure) - 3 or last_layer_dropout and i < len(layer_structure) - 2):
linears.append(torch.nn.Dropout(p=0.3)) linears.append(torch.nn.Dropout(p=0.3))
self.linear = torch.nn.Sequential(*linears) self.linear = torch.nn.Sequential(*linears)
...@@ -75,7 +76,7 @@ class HypernetworkModule(torch.nn.Module): ...@@ -75,7 +76,7 @@ class HypernetworkModule(torch.nn.Module):
w, b = layer.weight.data, layer.bias.data w, b = layer.weight.data, layer.bias.data
if weight_init == "Normal" or type(layer) == torch.nn.LayerNorm: if weight_init == "Normal" or type(layer) == torch.nn.LayerNorm:
normal_(w, mean=0.0, std=0.01) normal_(w, mean=0.0, std=0.01)
normal_(b, mean=0.0, std=0.005) normal_(b, mean=0.0, std=0)
elif weight_init == 'XavierUniform': elif weight_init == 'XavierUniform':
xavier_uniform_(w) xavier_uniform_(w)
zeros_(b) zeros_(b)
...@@ -127,7 +128,7 @@ class Hypernetwork: ...@@ -127,7 +128,7 @@ class Hypernetwork:
filename = None filename = None
name = None name = None
def __init__(self, name=None, enable_sizes=None, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False): def __init__(self, name=None, enable_sizes=None, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False, activate_output=False, **kwargs):
self.filename = None self.filename = None
self.name = name self.name = name
self.layers = {} self.layers = {}
...@@ -139,11 +140,15 @@ class Hypernetwork: ...@@ -139,11 +140,15 @@ class Hypernetwork:
self.weight_init = weight_init self.weight_init = weight_init
self.add_layer_norm = add_layer_norm self.add_layer_norm = add_layer_norm
self.use_dropout = use_dropout self.use_dropout = use_dropout
self.activate_output = activate_output
self.last_layer_dropout = kwargs['last_layer_dropout'] if 'last_layer_dropout' in kwargs else True
for size in enable_sizes or []: for size in enable_sizes or []:
self.layers[size] = ( self.layers[size] = (
HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init, self.add_layer_norm, self.use_dropout), HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init,
HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init, self.add_layer_norm, self.use_dropout), self.add_layer_norm, self.use_dropout, self.activate_output, last_layer_dropout=self.last_layer_dropout),
HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init,
self.add_layer_norm, self.use_dropout, self.activate_output, last_layer_dropout=self.last_layer_dropout),
) )
def weights(self): def weights(self):
...@@ -171,7 +176,9 @@ class Hypernetwork: ...@@ -171,7 +176,9 @@ class Hypernetwork:
state_dict['use_dropout'] = self.use_dropout state_dict['use_dropout'] = self.use_dropout
state_dict['sd_checkpoint'] = self.sd_checkpoint state_dict['sd_checkpoint'] = self.sd_checkpoint
state_dict['sd_checkpoint_name'] = self.sd_checkpoint_name state_dict['sd_checkpoint_name'] = self.sd_checkpoint_name
state_dict['activate_output'] = self.activate_output
state_dict['last_layer_dropout'] = self.last_layer_dropout
torch.save(state_dict, filename) torch.save(state_dict, filename)
def load(self, filename): def load(self, filename):
...@@ -191,12 +198,17 @@ class Hypernetwork: ...@@ -191,12 +198,17 @@ class Hypernetwork:
print(f"Layer norm is set to {self.add_layer_norm}") print(f"Layer norm is set to {self.add_layer_norm}")
self.use_dropout = state_dict.get('use_dropout', False) self.use_dropout = state_dict.get('use_dropout', False)
print(f"Dropout usage is set to {self.use_dropout}" ) print(f"Dropout usage is set to {self.use_dropout}" )
self.activate_output = state_dict.get('activate_output', True)
print(f"Activate last layer is set to {self.activate_output}")
self.last_layer_dropout = state_dict.get('last_layer_dropout', False)
for size, sd in state_dict.items(): for size, sd in state_dict.items():
if type(size) == int: if type(size) == int:
self.layers[size] = ( self.layers[size] = (
HypernetworkModule(size, sd[0], self.layer_structure, self.activation_func, self.weight_init, self.add_layer_norm, self.use_dropout), HypernetworkModule(size, sd[0], self.layer_structure, self.activation_func, self.weight_init,
HypernetworkModule(size, sd[1], self.layer_structure, self.activation_func, self.weight_init, self.add_layer_norm, self.use_dropout), self.add_layer_norm, self.use_dropout, self.activate_output, last_layer_dropout=self.last_layer_dropout),
HypernetworkModule(size, sd[1], self.layer_structure, self.activation_func, self.weight_init,
self.add_layer_norm, self.use_dropout, self.activate_output, last_layer_dropout=self.last_layer_dropout),
) )
self.name = state_dict.get('name', self.name) self.name = state_dict.get('name', self.name)
......
...@@ -49,7 +49,7 @@ def expand_crop_region(crop_region, processing_width, processing_height, image_w ...@@ -49,7 +49,7 @@ def expand_crop_region(crop_region, processing_width, processing_height, image_w
ratio_processing = processing_width / processing_height ratio_processing = processing_width / processing_height
if ratio_crop_region > ratio_processing: if ratio_crop_region > ratio_processing:
desired_height = (x2 - x1) * ratio_processing desired_height = (x2 - x1) / ratio_processing
desired_height_diff = int(desired_height - (y2-y1)) desired_height_diff = int(desired_height - (y2-y1))
y1 -= desired_height_diff//2 y1 -= desired_height_diff//2
y2 += desired_height_diff - desired_height_diff//2 y2 += desired_height_diff - desired_height_diff//2
......
...@@ -134,11 +134,7 @@ class StableDiffusionProcessing(): ...@@ -134,11 +134,7 @@ class StableDiffusionProcessing():
# Dummy zero conditioning if we're not using inpainting model. # Dummy zero conditioning if we're not using inpainting model.
# Still takes up a bit of memory, but no encoder call. # Still takes up a bit of memory, but no encoder call.
# Pretty sure we can just make this a 1x1 image since its not going to be used besides its batch size. # Pretty sure we can just make this a 1x1 image since its not going to be used besides its batch size.
return torch.zeros( return x.new_zeros(x.shape[0], 5, 1, 1)
x.shape[0], 5, 1, 1,
dtype=x.dtype,
device=x.device
)
height = height or self.height height = height or self.height
width = width or self.width width = width or self.width
...@@ -156,11 +152,7 @@ class StableDiffusionProcessing(): ...@@ -156,11 +152,7 @@ class StableDiffusionProcessing():
def img2img_image_conditioning(self, source_image, latent_image, image_mask = None): def img2img_image_conditioning(self, source_image, latent_image, image_mask = None):
if self.sampler.conditioning_key not in {'hybrid', 'concat'}: if self.sampler.conditioning_key not in {'hybrid', 'concat'}:
# Dummy zero conditioning if we're not using inpainting model. # Dummy zero conditioning if we're not using inpainting model.
return torch.zeros( return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1)
latent_image.shape[0], 5, 1, 1,
dtype=latent_image.dtype,
device=latent_image.device
)
# Handle the different mask inputs # Handle the different mask inputs
if image_mask is not None: if image_mask is not None:
...@@ -174,11 +166,11 @@ class StableDiffusionProcessing(): ...@@ -174,11 +166,11 @@ class StableDiffusionProcessing():
# Inpainting model uses a discretized mask as input, so we round to either 1.0 or 0.0 # Inpainting model uses a discretized mask as input, so we round to either 1.0 or 0.0
conditioning_mask = torch.round(conditioning_mask) conditioning_mask = torch.round(conditioning_mask)
else: else:
conditioning_mask = torch.ones(1, 1, *source_image.shape[-2:]) conditioning_mask = source_image.new_ones(1, 1, *source_image.shape[-2:])
# Create another latent image, this time with a masked version of the original input. # Create another latent image, this time with a masked version of the original input.
# Smoothly interpolate between the masked and unmasked latent conditioning image using a parameter. # Smoothly interpolate between the masked and unmasked latent conditioning image using a parameter.
conditioning_mask = conditioning_mask.to(source_image.device) conditioning_mask = conditioning_mask.to(source_image.device).to(source_image.dtype)
conditioning_image = torch.lerp( conditioning_image = torch.lerp(
source_image, source_image,
source_image * (1.0 - conditioning_mask), source_image * (1.0 - conditioning_mask),
...@@ -426,13 +418,13 @@ def process_images(p: StableDiffusionProcessing) -> Processed: ...@@ -426,13 +418,13 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
try: try:
for k, v in p.override_settings.items(): for k, v in p.override_settings.items():
opts.data[k] = v # we don't call onchange for simplicity which makes changing model, hypernet impossible setattr(opts, k, v) # we don't call onchange for simplicity which makes changing model, hypernet impossible
res = process_images_inner(p) res = process_images_inner(p)
finally: finally:
for k, v in stored_opts.items(): for k, v in stored_opts.items():
opts.data[k] = v setattr(opts, k, v)
return res return res
...@@ -673,10 +665,17 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): ...@@ -673,10 +665,17 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
images.save_image(image, self.outpath_samples, "", seeds[index], prompts[index], opts.samples_format, suffix="-before-highres-fix") images.save_image(image, self.outpath_samples, "", seeds[index], prompts[index], opts.samples_format, suffix="-before-highres-fix")
if opts.use_scale_latent_for_hires_fix: if opts.use_scale_latent_for_hires_fix:
samples = torch.nn.functional.interpolate(samples, size=(self.height // opt_f, self.width // opt_f), mode="bilinear")
for i in range(samples.shape[0]): for i in range(samples.shape[0]):
save_intermediate(samples, i) save_intermediate(samples, i)
samples = torch.nn.functional.interpolate(samples, size=(self.height // opt_f, self.width // opt_f), mode="bilinear")
# Avoid making the inpainting conditioning unless necessary as
# this does need some extra compute to decode / encode the image again.
if getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) < 1.0:
image_conditioning = self.img2img_image_conditioning(decode_first_stage(self.sd_model, samples), samples)
else:
image_conditioning = self.txt2img_image_conditioning(samples)
else: else:
decoded_samples = decode_first_stage(self.sd_model, samples) decoded_samples = decode_first_stage(self.sd_model, samples)
lowres_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0) lowres_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0)
...@@ -700,14 +699,14 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): ...@@ -700,14 +699,14 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
samples = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(decoded_samples)) samples = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(decoded_samples))
image_conditioning = self.img2img_image_conditioning(decoded_samples, samples)
shared.state.nextjob() shared.state.nextjob()
self.sampler = sd_samplers.create_sampler_with_index(sd_samplers.samplers, self.sampler_index, self.sd_model) self.sampler = sd_samplers.create_sampler_with_index(sd_samplers.samplers, self.sampler_index, self.sd_model)
noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, seed_resize_from_h=self.seed_resize_from_h, seed_resize_from_w=self.seed_resize_from_w, p=self) noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, seed_resize_from_h=self.seed_resize_from_h, seed_resize_from_w=self.seed_resize_from_w, p=self)
image_conditioning = self.txt2img_image_conditioning(x)
# GC now before running the next img2img to prevent running out of memory # GC now before running the next img2img to prevent running out of memory
x = None x = None
devices.torch_gc() devices.torch_gc()
......
...@@ -18,6 +18,9 @@ class Script: ...@@ -18,6 +18,9 @@ class Script:
args_to = None args_to = None
alwayson = False alwayson = False
"""A gr.Group component that has all script's UI inside it"""
group = None
infotext_fields = None infotext_fields = None
"""if set in ui(), this is a list of pairs of gradio component + text; the text will be used when """if set in ui(), this is a list of pairs of gradio component + text; the text will be used when
parsing infotext to set the value for the component; see ui.py's txt2img_paste_fields for an example parsing infotext to set the value for the component; see ui.py's txt2img_paste_fields for an example
...@@ -218,8 +221,6 @@ class ScriptRunner: ...@@ -218,8 +221,6 @@ class ScriptRunner:
for control in controls: for control in controls:
control.custom_script_source = os.path.basename(script.filename) control.custom_script_source = os.path.basename(script.filename)
if not script.alwayson:
control.visible = False
if script.infotext_fields is not None: if script.infotext_fields is not None:
self.infotext_fields += script.infotext_fields self.infotext_fields += script.infotext_fields
...@@ -229,40 +230,41 @@ class ScriptRunner: ...@@ -229,40 +230,41 @@ class ScriptRunner:
script.args_to = len(inputs) script.args_to = len(inputs)
for script in self.alwayson_scripts: for script in self.alwayson_scripts:
with gr.Group(): with gr.Group() as group:
create_script_ui(script, inputs, inputs_alwayson) create_script_ui(script, inputs, inputs_alwayson)
script.group = group
dropdown = gr.Dropdown(label="Script", elem_id="script_list", choices=["None"] + self.titles, value="None", type="index") dropdown = gr.Dropdown(label="Script", elem_id="script_list", choices=["None"] + self.titles, value="None", type="index")
dropdown.save_to_config = True dropdown.save_to_config = True
inputs[0] = dropdown inputs[0] = dropdown
for script in self.selectable_scripts: for script in self.selectable_scripts:
create_script_ui(script, inputs, inputs_alwayson) with gr.Group(visible=False) as group:
create_script_ui(script, inputs, inputs_alwayson)
script.group = group
def select_script(script_index): def select_script(script_index):
if 0 < script_index <= len(self.selectable_scripts): selected_script = self.selectable_scripts[script_index - 1] if script_index>0 else None
script = self.selectable_scripts[script_index-1]
args_from = script.args_from
args_to = script.args_to
else:
args_from = 0
args_to = 0
return [ui.gr_show(True if i == 0 else args_from <= i < args_to or is_alwayson) for i, is_alwayson in enumerate(inputs_alwayson)] return [gr.update(visible=selected_script == s) for s in self.selectable_scripts]
def init_field(title): def init_field(title):
"""called when an initial value is set from ui-config.json to show script's UI components"""
if title == 'None': if title == 'None':
return return
script_index = self.titles.index(title) script_index = self.titles.index(title)
script = self.selectable_scripts[script_index] self.selectable_scripts[script_index].group.visible = True
for i in range(script.args_from, script.args_to):
inputs[i].visible = True
dropdown.init_field = init_field dropdown.init_field = init_field
dropdown.change( dropdown.change(
fn=select_script, fn=select_script,
inputs=[dropdown], inputs=[dropdown],
outputs=inputs outputs=[script.group for script in self.selectable_scripts]
) )
return inputs return inputs
......
...@@ -163,11 +163,11 @@ def load_model_weights(model, checkpoint_info, vae_file="auto"): ...@@ -163,11 +163,11 @@ def load_model_weights(model, checkpoint_info, vae_file="auto"):
checkpoint_file = checkpoint_info.filename checkpoint_file = checkpoint_info.filename
sd_model_hash = checkpoint_info.hash sd_model_hash = checkpoint_info.hash
vae_file = sd_vae.resolve_vae(checkpoint_file, vae_file=vae_file) if shared.opts.sd_checkpoint_cache > 0 and hasattr(model, "sd_checkpoint_info"):
sd_vae.restore_base_vae(model)
checkpoints_loaded[model.sd_checkpoint_info] = model.state_dict().copy()
checkpoint_key = checkpoint_info if checkpoint_info not in checkpoints_loaded:
if checkpoint_key not in checkpoints_loaded:
print(f"Loading weights [{sd_model_hash}] from {checkpoint_file}") print(f"Loading weights [{sd_model_hash}] from {checkpoint_file}")
pl_sd = torch.load(checkpoint_file, map_location=shared.weight_load_location) pl_sd = torch.load(checkpoint_file, map_location=shared.weight_load_location)
...@@ -197,17 +197,15 @@ def load_model_weights(model, checkpoint_info, vae_file="auto"): ...@@ -197,17 +197,15 @@ def load_model_weights(model, checkpoint_info, vae_file="auto"):
model.first_stage_model.to(devices.dtype_vae) model.first_stage_model.to(devices.dtype_vae)
if shared.opts.sd_checkpoint_cache > 0:
# if PR #4035 were to get merged, restore base VAE first before caching
checkpoints_loaded[checkpoint_key] = model.state_dict().copy()
while len(checkpoints_loaded) > shared.opts.sd_checkpoint_cache:
checkpoints_loaded.popitem(last=False) # LRU
else: else:
vae_name = sd_vae.get_filename(vae_file) vae_name = sd_vae.get_filename(vae_file) if vae_file else None
print(f"Loading weights [{sd_model_hash}] with {vae_name} VAE from cache") vae_message = f" with {vae_name} VAE" if vae_name else ""
checkpoints_loaded.move_to_end(checkpoint_key) print(f"Loading weights [{sd_model_hash}]{vae_message} from cache")
model.load_state_dict(checkpoints_loaded[checkpoint_key]) model.load_state_dict(checkpoints_loaded[checkpoint_info])
if shared.opts.sd_checkpoint_cache > 0:
while len(checkpoints_loaded) > shared.opts.sd_checkpoint_cache:
checkpoints_loaded.popitem(last=False) # LRU
model.sd_model_hash = sd_model_hash model.sd_model_hash = sd_model_hash
model.sd_model_checkpoint = checkpoint_file model.sd_model_checkpoint = checkpoint_file
......
...@@ -44,6 +44,7 @@ parser.add_argument("--precision", type=str, help="evaluate at this precision", ...@@ -44,6 +44,7 @@ parser.add_argument("--precision", type=str, help="evaluate at this precision",
parser.add_argument("--share", action='store_true', help="use share=True for gradio and make the UI accessible through their site") parser.add_argument("--share", action='store_true', help="use share=True for gradio and make the UI accessible through their site")
parser.add_argument("--ngrok", type=str, help="ngrok authtoken, alternative to gradio --share", default=None) parser.add_argument("--ngrok", type=str, help="ngrok authtoken, alternative to gradio --share", default=None)
parser.add_argument("--ngrok-region", type=str, help="The region in which ngrok should start.", default="us") parser.add_argument("--ngrok-region", type=str, help="The region in which ngrok should start.", default="us")
parser.add_argument("--enable-insecure-extension-access", action='store_true', help="enable extensions tab regardless of other options")
parser.add_argument("--codeformer-models-path", type=str, help="Path to directory with codeformer model file(s).", default=os.path.join(models_path, 'Codeformer')) parser.add_argument("--codeformer-models-path", type=str, help="Path to directory with codeformer model file(s).", default=os.path.join(models_path, 'Codeformer'))
parser.add_argument("--gfpgan-models-path", type=str, help="Path to directory with GFPGAN model file(s).", default=os.path.join(models_path, 'GFPGAN')) parser.add_argument("--gfpgan-models-path", type=str, help="Path to directory with GFPGAN model file(s).", default=os.path.join(models_path, 'GFPGAN'))
parser.add_argument("--esrgan-models-path", type=str, help="Path to directory with ESRGAN model file(s).", default=os.path.join(models_path, 'ESRGAN')) parser.add_argument("--esrgan-models-path", type=str, help="Path to directory with ESRGAN model file(s).", default=os.path.join(models_path, 'ESRGAN'))
...@@ -99,7 +100,7 @@ restricted_opts = { ...@@ -99,7 +100,7 @@ restricted_opts = {
"outdir_save", "outdir_save",
} }
cmd_opts.disable_extension_access = cmd_opts.share or cmd_opts.listen cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen) and not cmd_opts.enable_insecure_extension_access
devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_swinir, devices.device_esrgan, devices.device_scunet, devices.device_codeformer = \ devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_swinir, devices.device_esrgan, devices.device_scunet, devices.device_codeformer = \
(devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'swinir', 'esrgan', 'scunet', 'codeformer']) (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'swinir', 'esrgan', 'scunet', 'codeformer'])
...@@ -146,6 +147,9 @@ class State: ...@@ -146,6 +147,9 @@ class State:
self.interrupted = True self.interrupted = True
def nextjob(self): def nextjob(self):
if opts.show_progress_every_n_steps == -1:
self.do_set_current_image()
self.job_no += 1 self.job_no += 1
self.sampling_step = 0 self.sampling_step = 0
self.current_image_sampling_step = 0 self.current_image_sampling_step = 0
...@@ -186,17 +190,21 @@ class State: ...@@ -186,17 +190,21 @@ class State:
"""sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this""" """sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this"""
def set_current_image(self): def set_current_image(self):
if self.sampling_step - self.current_image_sampling_step >= opts.show_progress_every_n_steps and opts.show_progress_every_n_steps > 0:
self.do_set_current_image()
def do_set_current_image(self):
if not parallel_processing_allowed: if not parallel_processing_allowed:
return return
if self.current_latent is None:
return
if opts.show_progress_grid:
self.current_image = sd_samplers.samples_to_image_grid(self.current_latent)
else:
self.current_image = sd_samplers.sample_to_image(self.current_latent)
if self.sampling_step - self.current_image_sampling_step >= opts.show_progress_every_n_steps and self.current_latent is not None: self.current_image_sampling_step = self.sampling_step
if opts.show_progress_grid:
self.current_image = sd_samplers.samples_to_image_grid(self.current_latent)
else:
self.current_image = sd_samplers.sample_to_image(self.current_latent)
self.current_image_sampling_step = self.sampling_step
state = State() state = State()
...@@ -351,7 +359,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"), ...@@ -351,7 +359,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"),
options_templates.update(options_section(('ui', "User interface"), { options_templates.update(options_section(('ui', "User interface"), {
"show_progressbar": OptionInfo(True, "Show progressbar"), "show_progressbar": OptionInfo(True, "Show progressbar"),
"show_progress_every_n_steps": OptionInfo(0, "Show image creation progress every N sampling steps. Set 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 32, "step": 1}), "show_progress_every_n_steps": OptionInfo(0, "Show image creation progress every N sampling steps. Set to 0 to disable. Set to -1 to show after completion of batch.", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}),
"show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"), "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"),
"return_grid": OptionInfo(True, "Show grid in results for web"), "return_grid": OptionInfo(True, "Show grid in results for web"),
"do_not_show_images": OptionInfo(False, "Do not show any images in results for web"), "do_not_show_images": OptionInfo(False, "Do not show any images in results for web"),
...@@ -396,6 +404,15 @@ class Options: ...@@ -396,6 +404,15 @@ class Options:
def __setattr__(self, key, value): def __setattr__(self, key, value):
if self.data is not None: if self.data is not None:
if key in self.data or key in self.data_labels: if key in self.data or key in self.data_labels:
assert not cmd_opts.freeze_settings, "changing settings is disabled"
comp_args = opts.data_labels[key].component_args
if isinstance(comp_args, dict) and comp_args.get('visible', True) is False:
raise RuntimeError(f"not possible to set {key} because it is restricted")
if cmd_opts.hide_ui_dir_config and key in restricted_opts:
raise RuntimeError(f"not possible to set {key} because it is restricted")
self.data[key] = value self.data[key] = value
return return
...@@ -412,6 +429,8 @@ class Options: ...@@ -412,6 +429,8 @@ class Options:
return super(Options, self).__getattribute__(item) return super(Options, self).__getattribute__(item)
def save(self, filename): def save(self, filename):
assert not cmd_opts.freeze_settings, "saving settings is disabled"
with open(filename, "w", encoding="utf8") as file: with open(filename, "w", encoding="utf8") as file:
json.dump(self.data, file, indent=4) json.dump(self.data, file, indent=4)
......
...@@ -276,7 +276,7 @@ def check_progress_call(id_part): ...@@ -276,7 +276,7 @@ def check_progress_call(id_part):
image = gr_show(False) image = gr_show(False)
preview_visibility = gr_show(False) preview_visibility = gr_show(False)
if opts.show_progress_every_n_steps > 0: if opts.show_progress_every_n_steps != 0:
shared.state.set_current_image() shared.state.set_current_image()
image = shared.state.current_image image = shared.state.current_image
...@@ -1052,6 +1052,8 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1052,6 +1052,8 @@ def create_ui(wrap_gradio_gpu_call):
extras_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs, placeholder="Leave blank to save images to the default path.") extras_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs, placeholder="Leave blank to save images to the default path.")
show_extras_results = gr.Checkbox(label='Show result images', value=True) show_extras_results = gr.Checkbox(label='Show result images', value=True)
submit = gr.Button('Generate', elem_id="extras_generate", variant='primary')
with gr.Tabs(elem_id="extras_resize_mode"): with gr.Tabs(elem_id="extras_resize_mode"):
with gr.TabItem('Scale by'): with gr.TabItem('Scale by'):
upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4) upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4)
...@@ -1079,8 +1081,6 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1079,8 +1081,6 @@ def create_ui(wrap_gradio_gpu_call):
with gr.Group(): with gr.Group():
upscale_before_face_fix = gr.Checkbox(label='Upscale Before Restoring Faces', value=False) upscale_before_face_fix = gr.Checkbox(label='Upscale Before Restoring Faces', value=False)
submit = gr.Button('Generate', elem_id="extras_generate", variant='primary')
result_images, html_info_x, html_info = create_output_panel("extras", opts.outdir_extras_samples) result_images, html_info_x, html_info = create_output_panel("extras", opts.outdir_extras_samples)
submit.click( submit.click(
...@@ -1182,8 +1182,8 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1182,8 +1182,8 @@ def create_ui(wrap_gradio_gpu_call):
new_hypernetwork_name = gr.Textbox(label="Name") new_hypernetwork_name = gr.Textbox(label="Name")
new_hypernetwork_sizes = gr.CheckboxGroup(label="Modules", value=["768", "320", "640", "1280"], choices=["768", "320", "640", "1280"]) new_hypernetwork_sizes = gr.CheckboxGroup(label="Modules", value=["768", "320", "640", "1280"], choices=["768", "320", "640", "1280"])
new_hypernetwork_layer_structure = gr.Textbox("1, 2, 1", label="Enter hypernetwork layer structure", placeholder="1st and last digit must be 1. ex:'1, 2, 1'") new_hypernetwork_layer_structure = gr.Textbox("1, 2, 1", label="Enter hypernetwork layer structure", placeholder="1st and last digit must be 1. ex:'1, 2, 1'")
new_hypernetwork_activation_func = gr.Dropdown(value="linear", label="Select activation function of hypernetwork", choices=modules.hypernetworks.ui.keys) new_hypernetwork_activation_func = gr.Dropdown(value="linear", label="Select activation function of hypernetwork. Recommended : Swish / Linear(none)", choices=modules.hypernetworks.ui.keys)
new_hypernetwork_initialization_option = gr.Dropdown(value = "Normal", label="Select Layer weights initialization. relu-like - Kaiming, sigmoid-like - Xavier is recommended", choices=["Normal", "KaimingUniform", "KaimingNormal", "XavierUniform", "XavierNormal"]) new_hypernetwork_initialization_option = gr.Dropdown(value = "Normal", label="Select Layer weights initialization. Recommended: Kaiming for relu-like, Xavier for sigmoid-like, Normal otherwise", choices=["Normal", "KaimingUniform", "KaimingNormal", "XavierUniform", "XavierNormal"])
new_hypernetwork_add_layer_norm = gr.Checkbox(label="Add layer normalization") new_hypernetwork_add_layer_norm = gr.Checkbox(label="Add layer normalization")
new_hypernetwork_use_dropout = gr.Checkbox(label="Use dropout") new_hypernetwork_use_dropout = gr.Checkbox(label="Use dropout")
overwrite_old_hypernetwork = gr.Checkbox(value=False, label="Overwrite Old Hypernetwork") overwrite_old_hypernetwork = gr.Checkbox(value=False, label="Overwrite Old Hypernetwork")
...@@ -1438,25 +1438,16 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1438,25 +1438,16 @@ def create_ui(wrap_gradio_gpu_call):
def run_settings(*args): def run_settings(*args):
changed = 0 changed = 0
assert not shared.cmd_opts.freeze_settings, "changing settings is disabled"
for key, value, comp in zip(opts.data_labels.keys(), args, components): for key, value, comp in zip(opts.data_labels.keys(), args, components):
if comp != dummy_component and not opts.same_type(value, opts.data_labels[key].default): assert comp == dummy_component or opts.same_type(value, opts.data_labels[key].default), f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
return f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}", opts.dumpjson()
for key, value, comp in zip(opts.data_labels.keys(), args, components): for key, value, comp in zip(opts.data_labels.keys(), args, components):
if comp == dummy_component: if comp == dummy_component:
continue continue
comp_args = opts.data_labels[key].component_args
if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False:
continue
if cmd_opts.hide_ui_dir_config and key in restricted_opts:
continue
oldval = opts.data.get(key, None) oldval = opts.data.get(key, None)
opts.data[key] = value
setattr(opts, key, value)
if oldval != value: if oldval != value:
if opts.data_labels[key].onchange is not None: if opts.data_labels[key].onchange is not None:
...@@ -1466,20 +1457,18 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1466,20 +1457,18 @@ def create_ui(wrap_gradio_gpu_call):
opts.save(shared.config_filename) opts.save(shared.config_filename)
return f'{changed} settings changed.', opts.dumpjson() return opts.dumpjson(), f'{changed} settings changed.'
def run_settings_single(value, key): def run_settings_single(value, key):
assert not shared.cmd_opts.freeze_settings, "changing settings is disabled"
if not opts.same_type(value, opts.data_labels[key].default): if not opts.same_type(value, opts.data_labels[key].default):
return gr.update(visible=True), opts.dumpjson() return gr.update(visible=True), opts.dumpjson()
oldval = opts.data.get(key, None) oldval = opts.data.get(key, None)
if cmd_opts.hide_ui_dir_config and key in restricted_opts: try:
setattr(opts, key, value)
except Exception:
return gr.update(value=oldval), opts.dumpjson() return gr.update(value=oldval), opts.dumpjson()
opts.data[key] = value
if oldval != value: if oldval != value:
if opts.data_labels[key].onchange is not None: if opts.data_labels[key].onchange is not None:
opts.data_labels[key].onchange() opts.data_labels[key].onchange()
...@@ -1632,9 +1621,9 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1632,9 +1621,9 @@ def create_ui(wrap_gradio_gpu_call):
text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False) text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
settings_submit.click( settings_submit.click(
fn=run_settings, fn=wrap_gradio_call(run_settings, extra_outputs=[gr.update()]),
inputs=components, inputs=components,
outputs=[result, text_settings], outputs=[text_settings, result],
) )
for i, k, item in quicksettings_list: for i, k, item in quicksettings_list:
......
...@@ -14,7 +14,7 @@ class Script(scripts.Script): ...@@ -14,7 +14,7 @@ class Script(scripts.Script):
return cmd_opts.allow_code return cmd_opts.allow_code
def ui(self, is_img2img): def ui(self, is_img2img):
code = gr.Textbox(label="Python code", visible=False, lines=1) code = gr.Textbox(label="Python code", lines=1)
return [code] return [code]
......
...@@ -132,7 +132,7 @@ class Script(scripts.Script): ...@@ -132,7 +132,7 @@ class Script(scripts.Script):
info = gr.HTML("<p style=\"margin-bottom:0.75em\">Recommended settings: Sampling Steps: 80-100, Sampler: Euler a, Denoising strength: 0.8</p>") info = gr.HTML("<p style=\"margin-bottom:0.75em\">Recommended settings: Sampling Steps: 80-100, Sampler: Euler a, Denoising strength: 0.8</p>")
pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128) pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128)
mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=8, visible=False) mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=8)
direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down']) direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'])
noise_q = gr.Slider(label="Fall-off exponent (lower=higher detail)", minimum=0.0, maximum=4.0, step=0.01, value=1.0) noise_q = gr.Slider(label="Fall-off exponent (lower=higher detail)", minimum=0.0, maximum=4.0, step=0.01, value=1.0)
color_variation = gr.Slider(label="Color variation", minimum=0.0, maximum=1.0, step=0.01, value=0.05) color_variation = gr.Slider(label="Color variation", minimum=0.0, maximum=1.0, step=0.01, value=0.05)
......
...@@ -22,8 +22,8 @@ class Script(scripts.Script): ...@@ -22,8 +22,8 @@ class Script(scripts.Script):
return None return None
pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128) pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128)
mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, visible=False) mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4)
inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", visible=False) inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index")
direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down']) direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'])
return [pixels, mask_blur, inpainting_fill, direction] return [pixels, mask_blur, inpainting_fill, direction]
......
...@@ -83,13 +83,14 @@ def cmdargs(line): ...@@ -83,13 +83,14 @@ def cmdargs(line):
def load_prompt_file(file): def load_prompt_file(file):
if (file is None): if file is None:
lines = [] lines = []
else: else:
lines = [x.strip() for x in file.decode('utf8', errors='ignore').split("\n")] lines = [x.strip() for x in file.decode('utf8', errors='ignore').split("\n")]
return None, "\n".join(lines), gr.update(lines=7) return None, "\n".join(lines), gr.update(lines=7)
class Script(scripts.Script): class Script(scripts.Script):
def title(self): def title(self):
return "Prompts from file or textbox" return "Prompts from file or textbox"
...@@ -107,9 +108,9 @@ class Script(scripts.Script): ...@@ -107,9 +108,9 @@ class Script(scripts.Script):
# We don't shrink back to 1, because that causes the control to ignore [enter], and it may # We don't shrink back to 1, because that causes the control to ignore [enter], and it may
# be unclear to the user that shift-enter is needed. # be unclear to the user that shift-enter is needed.
prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt]) prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt])
return [checkbox_iterate, checkbox_iterate_batch, file, prompt_txt] return [checkbox_iterate, checkbox_iterate_batch, prompt_txt]
def run(self, p, checkbox_iterate, checkbox_iterate_batch, file, prompt_txt: str): def run(self, p, checkbox_iterate, checkbox_iterate_batch, prompt_txt: str):
lines = [x.strip() for x in prompt_txt.splitlines()] lines = [x.strip() for x in prompt_txt.splitlines()]
lines = [x for x in lines if len(x) > 0] lines = [x for x in lines if len(x) > 0]
...@@ -157,5 +158,4 @@ class Script(scripts.Script): ...@@ -157,5 +158,4 @@ class Script(scripts.Script):
if checkbox_iterate: if checkbox_iterate:
p.seed = p.seed + (p.batch_size * p.n_iter) p.seed = p.seed + (p.batch_size * p.n_iter)
return Processed(p, images, p.seed, "")
return Processed(p, images, p.seed, "")
\ No newline at end of file
...@@ -18,8 +18,8 @@ class Script(scripts.Script): ...@@ -18,8 +18,8 @@ class Script(scripts.Script):
def ui(self, is_img2img): def ui(self, is_img2img):
info = gr.HTML("<p style=\"margin-bottom:0.75em\">Will upscale the image to twice the dimensions; use width and height sliders to set tile size</p>") info = gr.HTML("<p style=\"margin-bottom:0.75em\">Will upscale the image to twice the dimensions; use width and height sliders to set tile size</p>")
overlap = gr.Slider(minimum=0, maximum=256, step=16, label='Tile overlap', value=64, visible=False) overlap = gr.Slider(minimum=0, maximum=256, step=16, label='Tile overlap', value=64)
upscaler_index = gr.Radio(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index", visible=False) upscaler_index = gr.Radio(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index")
return [info, overlap, upscaler_index] return [info, overlap, upscaler_index]
......
...@@ -263,12 +263,12 @@ class Script(scripts.Script): ...@@ -263,12 +263,12 @@ class Script(scripts.Script):
current_axis_options = [x for x in axis_options if type(x) == AxisOption or type(x) == AxisOptionImg2Img and is_img2img] current_axis_options = [x for x in axis_options if type(x) == AxisOption or type(x) == AxisOptionImg2Img and is_img2img]
with gr.Row(): with gr.Row():
x_type = gr.Dropdown(label="X type", choices=[x.label for x in current_axis_options], value=current_axis_options[1].label, visible=False, type="index", elem_id="x_type") x_type = gr.Dropdown(label="X type", choices=[x.label for x in current_axis_options], value=current_axis_options[1].label, type="index", elem_id="x_type")
x_values = gr.Textbox(label="X values", visible=False, lines=1) x_values = gr.Textbox(label="X values", lines=1)
with gr.Row(): with gr.Row():
y_type = gr.Dropdown(label="Y type", choices=[x.label for x in current_axis_options], value=current_axis_options[0].label, visible=False, type="index", elem_id="y_type") y_type = gr.Dropdown(label="Y type", choices=[x.label for x in current_axis_options], value=current_axis_options[0].label, type="index", elem_id="y_type")
y_values = gr.Textbox(label="Y values", visible=False, lines=1) y_values = gr.Textbox(label="Y values", lines=1)
draw_legend = gr.Checkbox(label='Draw legend', value=True) draw_legend = gr.Checkbox(label='Draw legend', value=True)
include_lone_images = gr.Checkbox(label='Include Separate Images', value=False) include_lone_images = gr.Checkbox(label='Include Separate Images', value=False)
......
import unittest
import requests
class UtilsTests(unittest.TestCase):
def setUp(self):
self.url_options = "http://localhost:7860/sdapi/v1/options"
self.url_cmd_flags = "http://localhost:7860/sdapi/v1/cmd-flags"
self.url_samplers = "http://localhost:7860/sdapi/v1/samplers"
self.url_upscalers = "http://localhost:7860/sdapi/v1/upscalers"
self.url_sd_models = "http://localhost:7860/sdapi/v1/sd-models"
self.url_hypernetworks = "http://localhost:7860/sdapi/v1/hypernetworks"
self.url_face_restorers = "http://localhost:7860/sdapi/v1/face-restorers"
self.url_realesrgan_models = "http://localhost:7860/sdapi/v1/realesrgan-models"
self.url_prompt_styles = "http://localhost:7860/sdapi/v1/prompt-styles"
self.url_artist_categories = "http://localhost:7860/sdapi/v1/artist-categories"
self.url_artists = "http://localhost:7860/sdapi/v1/artists"
def test_options_get(self):
self.assertEqual(requests.get(self.url_options).status_code, 200)
def test_options_write(self):
response = requests.get(self.url_options)
self.assertEqual(response.status_code, 200)
pre_value = response.json()["send_seed"]
self.assertEqual(requests.post(self.url_options, json={"send_seed":not pre_value}).status_code, 200)
response = requests.get(self.url_options)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["send_seed"], not pre_value)
requests.post(self.url_options, json={"send_seed": pre_value})
def test_cmd_flags(self):
self.assertEqual(requests.get(self.url_cmd_flags).status_code, 200)
def test_samplers(self):
self.assertEqual(requests.get(self.url_samplers).status_code, 200)
def test_upscalers(self):
self.assertEqual(requests.get(self.url_upscalers).status_code, 200)
def test_sd_models(self):
self.assertEqual(requests.get(self.url_sd_models).status_code, 200)
def test_hypernetworks(self):
self.assertEqual(requests.get(self.url_hypernetworks).status_code, 200)
def test_face_restorers(self):
self.assertEqual(requests.get(self.url_face_restorers).status_code, 200)
def test_realesrgan_models(self):
self.assertEqual(requests.get(self.url_realesrgan_models).status_code, 200)
def test_prompt_styles(self):
self.assertEqual(requests.get(self.url_prompt_styles).status_code, 200)
def test_artist_categories(self):
self.assertEqual(requests.get(self.url_artist_categories).status_code, 200)
def test_artists(self):
self.assertEqual(requests.get(self.url_artists).status_code, 200)
\ No newline at end of file
...@@ -141,6 +141,12 @@ def webui(): ...@@ -141,6 +141,12 @@ def webui():
# after initial launch, disable --autolaunch for subsequent restarts # after initial launch, disable --autolaunch for subsequent restarts
cmd_opts.autolaunch = False cmd_opts.autolaunch = False
# gradio uses a very open CORS policy via app.user_middleware, which makes it possible for
# an attacker to trick the user into opening a malicious HTML page, which makes a request to the
# running web ui and do whatever the attcker wants, including installing an extension and
# runnnig its code. We disable this here. Suggested by RyotaK.
app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware']
app.add_middleware(GZipMiddleware, minimum_size=1000) app.add_middleware(GZipMiddleware, minimum_size=1000)
if launch_api: if launch_api:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment