2023-08-09 06:38:51 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
import grpc
|
|
|
|
from concurrent import futures
|
|
|
|
import time
|
|
|
|
import backend_pb2
|
|
|
|
import backend_pb2_grpc
|
|
|
|
import argparse
|
|
|
|
import signal
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
|
|
|
|
# import diffusers
|
|
|
|
import torch
|
|
|
|
from torch import autocast
|
|
|
|
from diffusers import StableDiffusionXLPipeline, DPMSolverMultistepScheduler, StableDiffusionPipeline, DiffusionPipeline, EulerAncestralDiscreteScheduler
|
2023-08-14 21:12:00 +00:00
|
|
|
from diffusers.pipelines.stable_diffusion import safety_checker
|
2023-08-16 20:24:52 +00:00
|
|
|
from compel import Compel
|
2023-08-09 06:38:51 +00:00
|
|
|
|
|
|
|
_ONE_DAY_IN_SECONDS = 60 * 60 * 24
|
2023-08-16 20:24:52 +00:00
|
|
|
COMPEL=os.environ.get("COMPEL", "1") == "1"
|
2023-08-09 06:38:51 +00:00
|
|
|
|
2023-08-14 21:12:00 +00:00
|
|
|
# https://github.com/CompVis/stable-diffusion/issues/239#issuecomment-1627615287
|
|
|
|
def sc(self, clip_input, images) : return images, [False for i in images]
|
|
|
|
# edit the StableDiffusionSafetyChecker class so that, when called, it just returns the images and an array of True values
|
|
|
|
safety_checker.StableDiffusionSafetyChecker.forward = sc
|
|
|
|
|
2023-08-09 06:38:51 +00:00
|
|
|
# Implement the BackendServicer class with the service methods
|
|
|
|
class BackendServicer(backend_pb2_grpc.BackendServicer):
|
|
|
|
def Health(self, request, context):
|
|
|
|
return backend_pb2.Reply(message=bytes("OK", 'utf-8'))
|
|
|
|
def LoadModel(self, request, context):
|
|
|
|
try:
|
|
|
|
print(f"Loading model {request.Model}...", file=sys.stderr)
|
|
|
|
print(f"Request {request}", file=sys.stderr)
|
|
|
|
torchType = torch.float32
|
|
|
|
if request.F16Memory:
|
|
|
|
torchType = torch.float16
|
|
|
|
|
2023-08-14 21:12:00 +00:00
|
|
|
local = False
|
|
|
|
modelFile = request.Model
|
2023-08-15 23:11:42 +00:00
|
|
|
|
|
|
|
cfg_scale = 7
|
|
|
|
if request.CFGScale != 0:
|
|
|
|
cfg_scale = request.CFGScale
|
|
|
|
|
2023-08-14 21:12:00 +00:00
|
|
|
# Check if ModelFile exists
|
|
|
|
if request.ModelFile != "":
|
|
|
|
if os.path.exists(request.ModelFile):
|
|
|
|
local = True
|
|
|
|
modelFile = request.ModelFile
|
|
|
|
|
|
|
|
fromSingleFile = request.Model.startswith("http") or request.Model.startswith("/") or local
|
|
|
|
# If request.Model is a URL, use from_single_file
|
|
|
|
|
|
|
|
|
2023-08-09 06:38:51 +00:00
|
|
|
if request.PipelineType == "":
|
|
|
|
request.PipelineType == "StableDiffusionPipeline"
|
|
|
|
|
|
|
|
if request.PipelineType == "StableDiffusionPipeline":
|
2023-08-14 21:12:00 +00:00
|
|
|
if fromSingleFile:
|
|
|
|
self.pipe = StableDiffusionPipeline.from_single_file(modelFile,
|
2023-08-15 23:11:42 +00:00
|
|
|
torch_dtype=torchType,
|
|
|
|
guidance_scale=cfg_scale)
|
2023-08-14 21:12:00 +00:00
|
|
|
else:
|
|
|
|
self.pipe = StableDiffusionPipeline.from_pretrained(request.Model,
|
2023-08-15 23:11:42 +00:00
|
|
|
torch_dtype=torchType,
|
|
|
|
guidance_scale=cfg_scale)
|
2023-08-09 06:38:51 +00:00
|
|
|
|
|
|
|
if request.PipelineType == "DiffusionPipeline":
|
2023-08-14 21:12:00 +00:00
|
|
|
if fromSingleFile:
|
|
|
|
self.pipe = DiffusionPipeline.from_single_file(modelFile,
|
2023-08-15 23:11:42 +00:00
|
|
|
torch_dtype=torchType,
|
|
|
|
guidance_scale=cfg_scale)
|
2023-08-14 21:12:00 +00:00
|
|
|
else:
|
|
|
|
self.pipe = DiffusionPipeline.from_pretrained(request.Model,
|
2023-08-15 23:11:42 +00:00
|
|
|
torch_dtype=torchType,
|
|
|
|
guidance_scale=cfg_scale)
|
2023-08-09 06:38:51 +00:00
|
|
|
|
|
|
|
if request.PipelineType == "StableDiffusionXLPipeline":
|
2023-08-14 21:12:00 +00:00
|
|
|
if fromSingleFile:
|
|
|
|
self.pipe = StableDiffusionXLPipeline.from_single_file(modelFile,
|
2023-08-15 23:11:42 +00:00
|
|
|
torch_dtype=torchType, use_safetensors=True,
|
|
|
|
guidance_scale=cfg_scale)
|
2023-08-14 21:12:00 +00:00
|
|
|
else:
|
|
|
|
self.pipe = StableDiffusionXLPipeline.from_pretrained(
|
|
|
|
request.Model,
|
|
|
|
torch_dtype=torchType,
|
|
|
|
use_safetensors=True,
|
|
|
|
# variant="fp16"
|
2023-08-15 23:11:42 +00:00
|
|
|
guidance_scale=cfg_scale)
|
2023-08-09 06:38:51 +00:00
|
|
|
|
|
|
|
# torch_dtype needs to be customized. float16 for GPU, float32 for CPU
|
|
|
|
# TODO: this needs to be customized
|
|
|
|
if request.SchedulerType == "EulerAncestralDiscreteScheduler":
|
|
|
|
self.pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(self.pipe.scheduler.config)
|
|
|
|
if request.SchedulerType == "DPMSolverMultistepScheduler":
|
|
|
|
self.pipe.scheduler = DPMSolverMultistepScheduler.from_config(self.pipe.scheduler.config)
|
2023-08-15 23:11:42 +00:00
|
|
|
if request.SchedulerType == "DPMSolverMultistepScheduler++":
|
|
|
|
self.pipe.scheduler = DPMSolverMultistepScheduler.from_config(self.pipe.scheduler.config,algorithm_type="dpmsolver++")
|
|
|
|
if request.SchedulerType == "DPMSolverMultistepSchedulerSDE++":
|
|
|
|
self.pipe.scheduler = DPMSolverMultistepScheduler.from_config(self.pipe.scheduler.config, algorithm_type="sde-dpmsolver++")
|
2023-08-09 06:38:51 +00:00
|
|
|
if request.CUDA:
|
|
|
|
self.pipe.to('cuda')
|
2023-08-16 20:24:52 +00:00
|
|
|
|
|
|
|
self.compel = Compel(tokenizer=self.pipe.tokenizer, text_encoder=self.pipe.text_encoder)
|
2023-08-09 06:38:51 +00:00
|
|
|
except Exception as err:
|
|
|
|
return backend_pb2.Result(success=False, message=f"Unexpected {err=}, {type(err)=}")
|
|
|
|
# Implement your logic here for the LoadModel service
|
|
|
|
# Replace this with your desired response
|
|
|
|
return backend_pb2.Result(message="Model loaded successfully", success=True)
|
|
|
|
def GenerateImage(self, request, context):
|
|
|
|
|
|
|
|
prompt = request.positive_prompt
|
|
|
|
|
2023-08-14 21:12:00 +00:00
|
|
|
# create a dictionary of values for the parameters
|
|
|
|
options = {
|
|
|
|
"negative_prompt": request.negative_prompt,
|
|
|
|
"width": request.width,
|
|
|
|
"height": request.height,
|
|
|
|
"num_inference_steps": request.step
|
|
|
|
}
|
|
|
|
|
|
|
|
# Get the keys that we will build the args for our pipe for
|
|
|
|
keys = options.keys()
|
|
|
|
|
|
|
|
if request.EnableParameters != "":
|
|
|
|
keys = request.EnableParameters.split(",")
|
|
|
|
|
|
|
|
if request.EnableParameters == "none":
|
|
|
|
keys = []
|
|
|
|
|
|
|
|
# create a dictionary of parameters by using the keys from EnableParameters and the values from defaults
|
|
|
|
kwargs = {key: options[key] for key in keys}
|
2023-08-16 20:24:52 +00:00
|
|
|
image = {}
|
|
|
|
if COMPEL:
|
|
|
|
conditioning = self.compel.build_conditioning_tensor(prompt)
|
|
|
|
kwargs["prompt_embeds"]= conditioning
|
|
|
|
# pass the kwargs dictionary to the self.pipe method
|
|
|
|
image = self.pipe(
|
|
|
|
**kwargs
|
|
|
|
).images[0]
|
|
|
|
else:
|
|
|
|
# pass the kwargs dictionary to the self.pipe method
|
|
|
|
image = self.pipe(
|
|
|
|
prompt,
|
|
|
|
**kwargs
|
|
|
|
).images[0]
|
2023-08-09 06:38:51 +00:00
|
|
|
|
2023-08-14 21:12:00 +00:00
|
|
|
# save the result
|
2023-08-09 06:38:51 +00:00
|
|
|
image.save(request.dst)
|
|
|
|
|
|
|
|
return backend_pb2.Result(message="Model loaded successfully", success=True)
|
|
|
|
|
|
|
|
def serve(address):
|
|
|
|
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
|
|
|
|
backend_pb2_grpc.add_BackendServicer_to_server(BackendServicer(), server)
|
|
|
|
server.add_insecure_port(address)
|
|
|
|
server.start()
|
|
|
|
print("Server started. Listening on: " + address, file=sys.stderr)
|
|
|
|
|
|
|
|
# Define the signal handler function
|
|
|
|
def signal_handler(sig, frame):
|
|
|
|
print("Received termination signal. Shutting down...")
|
|
|
|
server.stop(0)
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
# Set the signal handlers for SIGINT and SIGTERM
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
|
|
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
time.sleep(_ONE_DAY_IN_SECONDS)
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
server.stop(0)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
parser = argparse.ArgumentParser(description="Run the gRPC server.")
|
|
|
|
parser.add_argument(
|
|
|
|
"--addr", default="localhost:50051", help="The address to bind the server to."
|
|
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
serve(args.addr)
|