add server class

This commit is contained in:
Vladimir Mandic
2023-06-17 13:44:55 -04:00
parent 5808b7a6ea
commit 8d80b5f6d9
9 changed files with 124 additions and 54 deletions
+4 -1
View File
@@ -66,7 +66,10 @@ def setup_logging(clean=False):
"inspect.value.border": "black",
}))
level = logging.DEBUG if args.debug else logging.INFO
logging.basicConfig(level=logging.ERROR, format='%(asctime)s | %(name)s | %(levelname)s | %(module)s | %(message)s', filename=log_file, filemode='a', encoding='utf-8', force=True)
try:
logging.basicConfig(level=logging.ERROR, format='%(asctime)s | %(name)s | %(levelname)s | %(module)s | %(message)s', filename=log_file, filemode='a', encoding='utf-8', force=True)
except Exception:
logging.basicConfig(level=logging.ERROR, format='%(asctime)s | %(name)s | %(levelname)s | %(module)s | %(message)s') # to be able to report unsupported python version
log.setLevel(logging.DEBUG) # log to file is always at level debug for facility `sd`
pretty_install(console=console)
traceback_install(console=console, extra_lines=1, width=console.width, word_wrap=False, indent_guides=False, suppress=[])
+3 -3
View File
@@ -566,9 +566,9 @@ table.settings-value-table td{
/* extra networks */
.extra-networks > div > [id *= '_extra_']{ margin: 0.3em; }
.extra-network-subdirs{ padding: 0.2em 0.35em; }
.extra-network-subdirs button{ margin: 0 0.15em; }
.extra-networks .tab-nav .search{ display: inline-block; max-width: 16em; margin: 0.3em; align-self: center; width: 16em; }
.extra-network-subdirs { padding: 0.2em 0.35em; }
.extra-network-subdirs button { margin: 0 0.15em; box-shadow: none; }
.extra-networks .tab-nav .search { display: inline-block; max-width: 16em; margin: 0.3em; align-self: center; width: 16em; }
#txt2img_extra_view, #img2img_extra_view { width: auto; }
.extra-network-cards .nocards, .extra-network-thumbs .nocards{ margin: 1.25em 0.5em 0.5em 0.5em; }
.extra-network-cards .nocards h1, .extra-network-thumbs .nocards h1{ font-size: 1.5em; margin-bottom: 1em; }
+3 -1
View File
@@ -191,10 +191,12 @@ if __name__ == "__main__":
while True:
try:
alive = instance.thread.is_alive()
requests = instance.server_state.total_requests if hasattr(instance, 'server_state') else 0
except Exception:
alive = False
requests = 0
if round(time.time()) % 120 == 0:
installer.log.debug(f'Server alive: {alive} Memory {get_memory_stats()}')
installer.log.debug(f'Server alive={alive} Requests={requests} memory {get_memory_stats()} ')
if not alive:
if instance.wants_restart:
installer.log.info('Server restarting...')
+15 -37
View File
@@ -1,7 +1,6 @@
import io
import time
import base64
import logging
from io import BytesIO
from typing import List, Dict, Any
from threading import Lock
@@ -637,40 +636,19 @@ class Api:
cuda = { 'error': f'{err}' }
return models.MemoryResponse(ram = ram, cuda = cuda)
def launch_uvicorn(self):
self.app.include_router(self.router)
import uvicorn
config: uvicorn.Config = {
"host": "0.0.0.0" if shared.cmd_opts.listen else "127.0.0.1",
"port": shared.cmd_opts.port if shared.cmd_opts.port else 7861,
"loop": "auto", # auto, asyncio, uvloop
"http": "auto", # auto, h11, httptools
"interface": "auto", # auto, asgi3, asgi2, wsgi
"ws": "auto", # auto, websockets, wsproto
"log_level": logging.WARNING,
"backlog": 4096, # default=2048
"timeout_keep_alive": 60, # default=5
"ssl_keyfile": shared.cmd_opts.tls_keyfile,
"ssl_certfile": shared.cmd_opts.tls_certfile,
}
shared.log.info(f'API server: Uvicorn options={config}')
uvicorn.run(self.app, **config)
def launch_hypercorn(self):
import asyncio
import hypercorn
import hypercorn.asyncio
config = hypercorn.config.Config()
config.bind = [f'{"0.0.0.0" if shared.cmd_opts.listen else "127.0.0.1"}:{shared.cmd_opts.port if shared.cmd_opts.port else 7861}']
config.keyfile = shared.cmd_opts.tls_keyfile
config.certfile = shared.cmd_opts.tls_certfile
config.keep_alive_timeout = 60 # default=5
config.backlog = 4096 # default=100
config.loglevel = "WARNING"
config.max_app_queue_size = 64 # default=10
shared.log.info(f'API server: Hypercorn options={vars(config)}')
instance = hypercorn.asyncio.serve(self.app, config)
asyncio.run(instance)
def launch(self):
self.launch_uvicorn()
config = {
"listen": shared.cmd_opts.listen,
"port": shared.cmd_opts.port,
"keyfile": shared.cmd_opts.tls_keyfile,
"certfile": shared.cmd_opts.tls_certfile,
"loop": "auto",
"http": "auto",
}
from modules.server import UvicornServer
server = UvicornServer(self.app, **config)
# from modules.server import HypercornServer
# server = HypercornServer(self.app, **config)
server.start()
shared.log.info(f'API server: Uvicorn options={config}')
return server
-1
View File
@@ -332,7 +332,6 @@ def read_state_dict(checkpoint_file, map_location=None): # pylint: disable=unuse
def get_checkpoint_state_dict(checkpoint_info: CheckpointInfo, timer):
if checkpoint_info in checkpoints_loaded:
# use checkpoint cache
shared.log.info("Model weights loading: from cache")
return checkpoints_loaded[checkpoint_info]
res = read_state_dict(checkpoint_info.filename)
+85
View File
@@ -0,0 +1,85 @@
import threading
import logging
import uvicorn
import fastapi
class UvicornServer(uvicorn.Server):
def __init__(self, app: fastapi.FastAPI, listen = None, port = None, keyfile = None, certfile = None, loop = "auto", http = "auto"):
self.app: fastapi.FastAPI = app
self.thread: threading.Thread = None
self.wants_restart = False
self.config = uvicorn.Config(
app=self.app,
host = "0.0.0.0" if listen else "127.0.0.1",
port = port or 7861,
loop = loop, # auto, asyncio, uvloop
http = http, # auto, h11, httptools
interface = "auto", # auto, asgi3, asgi2, wsgi
ws = "auto", # auto, websockets, wsproto
log_level = logging.WARNING,
backlog = 4096, # default=2048
timeout_keep_alive = 60, # default=5
ssl_keyfile = keyfile,
ssl_certfile = certfile,
ws_max_size = 1024 * 1024 * 1024, # default 16MB
)
super().__init__(config=self.config)
def start(self):
self.thread = threading.Thread(target=self.run, daemon=True)
self.wants_restart = False
self.thread.start()
def stop(self):
self.should_exit = True
self.thread.join()
def restart(self):
self.wants_restart = True
self.stop()
self.start()
class HypercornServer():
def __init__(self, app: fastapi.FastAPI, listen = None, port = None, keyfile = None, certfile = None, loop = "auto", http = None):
import asyncio
import hypercorn
self.app: fastapi.FastAPI = app
self.server: HypercornServer = None
self.thread = None
self.task = None
self.wants_restart = False
self.loop = 'trio' if loop == 'auto' else loop # asyncio, uvloop, trio
self.config = hypercorn.config.Config()
self.config.bind = [f'{"0.0.0.0" if listen else "127.0.0.1"}:{port or 7861}']
self.config.keyfile = keyfile
self.config.certfile = certfile
self.config.keep_alive_timeout = 60 # default=5
self.config.backlog = 4096 # default=100
self.config.loglevel = "WARNING"
self.config.max_app_queue_size = 64 # default=10
self.http = http # unused
self.main_loop = asyncio.get_event_loop()
def run(self):
import trio
from hypercorn.trio import serve
self.server = trio.run(serve, self.app, self.config)
def start(self):
if self.loop == 'trio':
self.thread = threading.Thread(target=self.run, daemon=True)
self.thread.start()
elif self.loop == 'asyncio': # does not run in thread
import asyncio
from hypercorn.asyncio import serve
self.server = serve(self.app, self.config)
asyncio.run(self.server)
elif self.loop == 'uvloop': # does not run in thread
import uvloop
from hypercorn.asyncio import serve
uvloop.install()
from hypercorn.asyncio import serve
self.server = serve(self.app, self.config)
asyncio.run(self.server)
+4 -4
View File
@@ -237,10 +237,10 @@ def create_toprow(is_img2img):
pause.click(fn=lambda: modules.shared.state.pause(), _js='checkPaused', inputs=[], outputs=[])
with gr.Row(elem_id=f"{id_part}_tools"):
paste = ToolButton(value=paste_symbol, elem_id="paste")
clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt")
extra_networks_button = ToolButton(value=extra_networks_symbol, elem_id=f"{id_part}_extra_networks")
prompt_style_apply = ToolButton(value=apply_style_symbol, elem_id=f"{id_part}_style_apply")
save_style = ToolButton(value=save_style_symbol, elem_id=f"{id_part}_style_create")
clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt_btn")
extra_networks_button = ToolButton(value=extra_networks_symbol, elem_id=f"{id_part}_extra_networks_btn")
prompt_style_apply = ToolButton(value=apply_style_symbol, elem_id=f"{id_part}_style_apply_btn")
save_style = ToolButton(value=save_style_symbol, elem_id=f"{id_part}_style_create_btn")
clear_prompt_button.click(fn=lambda *x: x, _js="confirm_clear_prompt", inputs=[prompt, negative_prompt], outputs=[prompt, negative_prompt])
with gr.Row(elem_id=f"{id_part}_counters"):
token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"])
+7 -5
View File
@@ -88,6 +88,10 @@ class ExtraNetworksPage:
return abspath[len(parentdir):].replace('\\', '/')
return ""
def is_empty(self, folder):
files = [f for f in os.listdir(folder) if f.lower().endswith(".ckpt") or f.lower().endswith(".safetensors") or f.lower().endswith(".pt")]
return len(files) == 0
def create_html(self, tabname):
view = shared.opts.extra_networks_default_view
items_html = ''
@@ -103,15 +107,13 @@ class ExtraNetworksPage:
subdir = os.path.abspath(x)[len(parentdir):].replace("\\", "/")
while subdir.startswith("/"):
subdir = subdir[1:]
is_empty = len(os.listdir(x)) == 0
if not is_empty and not subdir.endswith("/"):
subdir = subdir + "/"
subdirs[subdir] = 1
if not self.is_empty(x):
subdirs[subdir] = 1
if subdirs:
subdirs = {"": 1, **subdirs}
subdirs_html = "".join([f"""
<button class='lg secondary gradio-button custom-button{" search-all" if subdir=="" else ""}' onclick='extraNetworksSearchButton("{tabname}_extra_tabs", event)'>
{html.escape(subdir if subdir!="" else "all")}
{html.escape(subdir) if subdir!="" else "all"}
</button>""" for subdir in subdirs])
try:
self.items = list(self.list_items())
+3 -2
View File
@@ -258,6 +258,7 @@ def start_ui():
prevent_thread_lock=True,
max_threads=64,
show_api=True,
quiet=True,
favicon_path='html/logo.ico',
allowed_paths=[os.path.dirname(__file__), cmd_opts.data_dir],
app_kwargs=fastapi_args,
@@ -335,8 +336,8 @@ def api_only():
modules.script_callbacks.app_started_callback(None, app)
modules.sd_models.write_metadata()
log.info(f"Startup time: {startup_timer.summary()}")
api.launch()
return api
server = api.launch()
return server
if __name__ == "__main__":