mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 09:14:35 +02:00
switch to internally managed uvicorn
Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
+12
-1
@@ -1,7 +1,17 @@
|
||||
\# Change Log for SD.Next
|
||||
# Change Log for SD.Next
|
||||
|
||||
## Update for 2026-08-05
|
||||
|
||||
### Highlights for 2026-08-05
|
||||
|
||||
This release brings **Sefi-Image** and **Mage-Flow** models, plus a new **Nunchaku-Lite** inference engine
|
||||
On the server side, there are quite a few *under-the-hood* improvements, including optimized startup, optimized webserver, end-to-end profiling, storage analyzer, etc.
|
||||
There are also several new auxiliary models, such as **Lucida** for background removal
|
||||
And video processing now supports scripts such as prompt enhance, nudenet, etc.
|
||||
Plus several quality-of-life improvements and bug-fixes across the board
|
||||
|
||||
### Details for 2026-08-05
|
||||
|
||||
- **Models**
|
||||
- [SeFi-Image](https://huggingface.co/SeFi-Image/SeFi-Image-5B-RL) in *Base*, *Turbo* (distilled) and *RL* (finetuned) variants
|
||||
SeFi is an interesting model that separates generation into semantic and texture latent streams
|
||||
@@ -31,6 +41,7 @@
|
||||
- **API**
|
||||
- add `/sdapi/v1/storage` endpoint to return storage usage info
|
||||
- **Internal**
|
||||
- switch internal server to explicit `uvicorn`
|
||||
- update core requirements
|
||||
- **Fixes**
|
||||
- seedvr quality
|
||||
|
||||
+33
-11
@@ -1,36 +1,59 @@
|
||||
import threading
|
||||
import logging
|
||||
import time
|
||||
import asyncio
|
||||
import uvicorn
|
||||
import fastapi
|
||||
from modules.logger import log
|
||||
|
||||
|
||||
class UvicornServer(uvicorn.Server):
|
||||
def __init__(self, app: fastapi.FastAPI, listen = None, port = None, keyfile = None, certfile = None, loop = "auto", http = "auto"):
|
||||
def __init__(self, app: fastapi.FastAPI, host = None, listen = None, port = None, keyfile = None, certfile = None, loop = "auto", http = "auto"):
|
||||
self.app: fastapi.FastAPI = app
|
||||
self.thread: threading.Thread = None
|
||||
self.loop = None
|
||||
self.wants_restart = False
|
||||
self.should_exit = False
|
||||
kwargs = {
|
||||
'loop': loop, # auto, asyncio, uvloop
|
||||
'http': http, # auto, h11, httptools
|
||||
'interface': "auto", # auto, asgi3, asgi2, wsgi
|
||||
'ws': "auto", # auto, websockets, wsproto, websockets-sansio
|
||||
'timeout_keep_alive': 60, # default=5
|
||||
'ws_max_size': 1024 * 1024 * 1024, # default 16MB
|
||||
'ws_max_queue': 64, # default=32
|
||||
'ws_ping_interval': 30, # default=20
|
||||
'ws_ping_timeout': 60, # default=20
|
||||
'timeout_graceful_shutdown': 5, # default=None
|
||||
'access_log': False, # default=True
|
||||
'server_header': False, # default=True
|
||||
'date_header': False, # default=True
|
||||
'backlog': 4096, # default=2048
|
||||
'reload': False, # default=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
|
||||
host = host or ("0.0.0.0" if listen else "127.0.0.1"),
|
||||
port = port or 7860,
|
||||
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
|
||||
**kwargs
|
||||
)
|
||||
super().__init__(config=self.config)
|
||||
log.info(f'Server: uvicorn={kwargs}')
|
||||
|
||||
def start(self):
|
||||
self.thread = threading.Thread(target=self.run, daemon=True)
|
||||
self.wants_restart = False
|
||||
self.thread.start()
|
||||
start = time.time()
|
||||
while not self.started:
|
||||
time.sleep(1e-3)
|
||||
if time.time() - start > 5:
|
||||
raise RuntimeError("Server failed to start. Please check that the port is available.")
|
||||
policy = asyncio.get_event_loop_policy()
|
||||
self.loop = f"{type(policy).__module__}.{type(policy).__name__}"
|
||||
|
||||
def stop(self):
|
||||
self.should_exit = True
|
||||
@@ -71,7 +94,6 @@ class HypercornServer:
|
||||
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)
|
||||
|
||||
+3
-1
@@ -17,11 +17,13 @@ voluptuous
|
||||
fasteners
|
||||
limits
|
||||
orjson
|
||||
websockets
|
||||
ftfy
|
||||
websockets
|
||||
httptools
|
||||
|
||||
# versioned
|
||||
fastapi==0.124.4
|
||||
uvicorn==0.52.1
|
||||
rich==15.0.0
|
||||
safetensors==0.8.0
|
||||
peft==0.20.0
|
||||
|
||||
@@ -317,6 +317,41 @@ def mount_subpath(app):
|
||||
log.info(f'Mounted: subpath="{shared.opts.subpath}"')
|
||||
|
||||
|
||||
def start_server(
|
||||
blocks,
|
||||
server_name: str | None = None, # pylint: disable=redefined-outer-name
|
||||
server_port: int | None = None,
|
||||
ssl_keyfile: str | None = None,
|
||||
ssl_certfile: str | None = None,
|
||||
ssl_keyfile_password: str | None = None, # pylint: disable=unused-argument
|
||||
app_kwargs: dict | None = None,
|
||||
):
|
||||
from gradio.routes import App
|
||||
from modules.server import UvicornServer
|
||||
if ssl_keyfile is not None and ssl_certfile is None:
|
||||
raise ValueError("ssl_certfile must be provided if ssl_keyfile is provided.")
|
||||
server_name = server_name or "127.0.0.1"
|
||||
server_port = server_port or 7860
|
||||
url_host_name = "localhost" if server_name == "0.0.0.0" else server_name
|
||||
if server_name.startswith("[") and server_name.endswith("]"):
|
||||
server_name = server_name[1:-1]
|
||||
app = App.create_app(blocks, app_kwargs=app_kwargs)
|
||||
server = UvicornServer(
|
||||
app=app,
|
||||
listen=server_name == '0.0.0.0',
|
||||
host=server_name,
|
||||
port=server_port,
|
||||
keyfile=ssl_keyfile,
|
||||
certfile=ssl_certfile,
|
||||
)
|
||||
server.start()
|
||||
if ssl_keyfile is not None:
|
||||
path_to_local_server = f"https://{url_host_name}:{server_port}/"
|
||||
else:
|
||||
path_to_local_server = f"http://{url_host_name}:{server_port}/"
|
||||
return server_name, server_port, path_to_local_server, app, server
|
||||
|
||||
|
||||
def start_ui():
|
||||
log.debug('UI start sequence')
|
||||
log.debug(f'UI image support: kanvas={version.get("kanvas", "unknown")}')
|
||||
@@ -362,8 +397,10 @@ def start_ui():
|
||||
allowed_paths.append(shared.cmd_opts.models_dir)
|
||||
if shared.cmd_opts.allowed_paths is not None:
|
||||
allowed_paths += [p for p in shared.cmd_opts.allowed_paths if os.path.isdir(p)]
|
||||
log.debug(f'Root paths: {allowed_paths}')
|
||||
log.info(f'Server: name={server_name} port={shared.cmd_opts.port} paths={allowed_paths} ssl={shared.cmd_opts.tls_keyfile}:{shared.cmd_opts.tls_certfile} auth={len(auth_pairs)}')
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
import gradio.networking
|
||||
gradio.networking.start_server = start_server
|
||||
app, local_url, share_url = shared.demo.launch( # app is FastAPI(Starlette) instance
|
||||
share=shared.cmd_opts.share,
|
||||
server_name=server_name,
|
||||
@@ -382,6 +419,11 @@ def start_ui():
|
||||
app_kwargs=fastapi_args,
|
||||
_frontend=True and shared.cmd_opts.share,
|
||||
)
|
||||
|
||||
uc = shared.demo.server.config
|
||||
get_name = lambda c: getattr(c, '__name__', c) # pylint: disable=unnecessary-lambda-assignment
|
||||
log.debug(f'Server config: loop={shared.demo.server.loop} http={get_name(uc.http_protocol_class)} ws={get_name(uc.ws_protocol_class)} interface={uc.interface} workers={uc.workers} backlog={uc.backlog} timeout_keep_alive={uc.timeout_keep_alive} timeout_notify={uc.timeout_notify} ws_max_size={uc.ws_max_size} ws_max_queue={uc.ws_max_queue} ws_ping_interval={uc.ws_ping_interval} ws_ping_timeout={uc.ws_ping_timeout}')
|
||||
|
||||
if shared.cmd_opts.data_dir is not None:
|
||||
modules.gr_tempdir.register_tmp_file(shared.demo, os.path.join(shared.cmd_opts.data_dir, 'x'))
|
||||
log.info(f'Local URL: {local_url}')
|
||||
|
||||
Reference in New Issue
Block a user