diff --git a/installer.py b/installer.py
index f6bcda06c..ea8aca055 100644
--- a/installer.py
+++ b/installer.py
@@ -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=[])
diff --git a/javascript/style.css b/javascript/style.css
index b78c4da44..a80922445 100644
--- a/javascript/style.css
+++ b/javascript/style.css
@@ -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; }
diff --git a/launch.py b/launch.py
index 5ca0b1ccd..0f8ccaabe 100644
--- a/launch.py
+++ b/launch.py
@@ -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...')
diff --git a/modules/api/api.py b/modules/api/api.py
index 4d83d6f3a..57c808389 100644
--- a/modules/api/api.py
+++ b/modules/api/api.py
@@ -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
diff --git a/modules/sd_models.py b/modules/sd_models.py
index 79a0d9091..fc3a51fb8 100644
--- a/modules/sd_models.py
+++ b/modules/sd_models.py
@@ -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)
diff --git a/modules/server.py b/modules/server.py
new file mode 100644
index 000000000..14645a02d
--- /dev/null
+++ b/modules/server.py
@@ -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)
diff --git a/modules/ui.py b/modules/ui.py
index c7c106009..dc9a322e3 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -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="0/75", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"])
diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py
index d59e7202a..063a7faa2 100644
--- a/modules/ui_extra_networks.py
+++ b/modules/ui_extra_networks.py
@@ -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"""
""" for subdir in subdirs])
try:
self.items = list(self.list_items())
diff --git a/webui.py b/webui.py
index 093c0fdf4..9171e773a 100644
--- a/webui.py
+++ b/webui.py
@@ -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__":