update docker and progress monitoring

Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
Vladimir Mandic
2024-11-16 10:49:08 -05:00
parent 4e9e04b24c
commit 59cd08f5da
9 changed files with 88 additions and 18 deletions
+28 -10
View File
@@ -1,5 +1,10 @@
# SD.Next Dockerfile
# docs: <https://github.com/vladmandic/automatic/wiki/Docker>
# base image
FROM pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime
# metadata
LABEL org.opencontainers.image.vendor="SD.Next"
LABEL org.opencontainers.image.authors="vladmandic"
LABEL org.opencontainers.image.url="https://github.com/vladmandic/automatic/"
@@ -9,31 +14,44 @@ LABEL org.opencontainers.image.licenses="AGPL-3.0"
LABEL org.opencontainers.image.title="SD.Next"
LABEL org.opencontainers.image.description="SD.Next: Advanced Implementation of Stable Diffusion and other Diffusion-based generative image models"
LABEL org.opencontainers.image.base.name="https://hub.docker.com/pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime"
WORKDIR /
COPY . .
LABEL org.opencontainers.image.version="latest"
# minimum install
RUN ["apt-get", "-y", "update"]
RUN ["apt-get", "-y", "install", "git", "build-essential", "google-perftools", "curl"]
# optional if full cuda-dev is required by some downstream library
# RUN ["apt-get", "-y", "nvidia-cuda-toolkit"]
RUN ["/usr/sbin/ldconfig"]
# copy sdnext
COPY . /app
WORKDIR /app
# stop pip and uv from caching
ENV PIP_NO_CACHE_DIR=true
ENV PIP_ROOT_USER_ACTION=ignore
ENV UV_NO_CACHE=true
ENV SD_INSTALL_DEBUG=true
# disable model hashing for faster startup
ENV SD_NOHASHING=true
# set data directories
ENV SD_DATADIR="/mnt/data"
ENV SD_MODELSDIR="/mnt/models"
# minimum install
RUN ["apt-get", "-y", "update"]
RUN ["apt-get", "-y", "install", "git", "build-essential", "google-perftools"]
RUN ["ldconfig"]
ENV SD_DOCKER=true
# tcmalloc is not required but it is highly recommended
ENV LD_PRELOAD=libtcmalloc.so.4
# sdnext will run all necessary pip install ops and then exit
RUN ["python", "launch.py", "--debug", "--uv", "--use-cuda", "--log", "sdnext.log", "--test", "--optional"]
RUN ["python", "/app/launch.py", "--debug", "--uv", "--use-cuda", "--log", "sdnext.log", "--test", "--optional"]
# preinstall additional packages to avoid installation during runtime
# actually run sdnext
CMD ["python", "launch.py", "--debug", "--skip-all", "--listen", "--quick", "--api-log", "--log", "sdnext.log"]
# expose port
EXPOSE 7860
# TBD add healthcheck function
HEALTHCHECK NONE
# healthcheck function
# HEALTHCHECK --interval=60s --timeout=10s --start-period=60s --retries=3 CMD curl --fail http://localhost:7860/sdapi/v1/status || exit 1
# stop signal
STOPSIGNAL SIGINT
+3 -3
View File
@@ -412,14 +412,14 @@ def get_platform():
else:
release = platform.release()
return {
# 'host': platform.node(),
'arch': platform.machine(),
'cpu': platform.processor(),
'system': platform.system(),
'release': release,
# 'platform': platform.platform(aliased = True, terse = False),
# 'version': platform.version(),
'python': platform.python_version(),
'docker': os.environ.get('SD_INSTALL_DEBUG', None) is not None,
# 'host': platform.node(),
# 'version': platform.version(),
}
except Exception as e:
return { 'error': e }
+1 -3
View File
@@ -258,9 +258,7 @@ def main():
alive = False
requests = 0
if round(time.time()) % 120 == 0:
state = f'job="{instance.state.job}" {instance.state.job_no}/{instance.state.job_count}' if instance.state.job != '' or instance.state.job_no != 0 or instance.state.job_count != 0 else 'idle'
uptime = round(time.time() - instance.state.server_start)
installer.log.debug(f'Server: alive={alive} jobs={instance.state.total_jobs} requests={requests} uptime={uptime} memory={get_memory_stats()} backend={instance.backend} state={state}')
installer.log.debug(f'Server: alive={alive} requests={requests} memory={get_memory_stats()} {instance.state.status()}')
if not alive:
if uv is not None and uv.wants_restart:
installer.log.info('Server restarting...')
+1
View File
@@ -38,6 +38,7 @@ class Api:
self.add_api_route("/sdapi/v1/log", server.get_log_buffer, methods=["GET"], response_model=List[str])
self.add_api_route("/sdapi/v1/start", self.get_session_start, methods=["GET"])
self.add_api_route("/sdapi/v1/version", server.get_version, methods=["GET"])
self.add_api_route("/sdapi/v1/status", server.get_status, methods=["GET"], response_model=models.ResStatus)
self.add_api_route("/sdapi/v1/platform", server.get_platform, methods=["GET"])
self.add_api_route("/sdapi/v1/progress", server.get_progress, methods=["GET"], response_model=models.ResProgress)
self.add_api_route("/sdapi/v1/interrupt", server.post_interrupt, methods=["POST"])
+17
View File
@@ -300,6 +300,23 @@ class ResProgress(BaseModel):
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.")
textinfo: str = Field(default=None, title="Info text", description="Info text used by WebUI.")
class ResStatus(BaseModel):
status: str = Field(title="Status", description="Current status")
task: str = Field(title="Task", description="Current task")
timestamp: Optional[str] = Field(title="Timestamp", description="Timestamp of the current job")
id: str = Field(title="ID", description="ID of the current task")
job: int = Field(title="Job", description="Current job")
jobs: int = Field(title="Jobs", description="Total jobs")
total: int = Field(title="Total Jobs", description="Total jobs")
step: int = Field(title="Step", description="Current step")
steps: int = Field(title="Steps", description="Total steps")
queued: int = Field(title="Queued", description="Number of queued tasks")
uptime: int = Field(title="Uptime", description="Uptime of the server")
elapsed: Optional[float] = Field(title="Elapsed time")
eta: Optional[float] = Field(title="ETA in secs")
progress: Optional[float] = Field(title="Progress", description="The progress with a range of 0 to 1")
class ReqInterrogate(BaseModel):
image: str = Field(default="", title="Image", description="Image to work on, must be a Base64 string containing the image's data.")
clip_model: str = Field(default="", title="CLiP Model", description="The interrogate model used.")
+4 -1
View File
@@ -1,3 +1,4 @@
import time
from typing import Any, Dict
from fastapi import Depends
from modules import shared
@@ -66,7 +67,6 @@ def get_cmd_flags():
return vars(shared.cmd_opts)
def get_progress(req: models.ReqProgress = Depends()):
import time
if shared.state.job_count == 0:
return models.ResProgress(progress=0, eta_relative=0, state=shared.state.dict(), textinfo=shared.state.textinfo)
shared.state.do_set_current_image()
@@ -85,6 +85,9 @@ def get_progress(req: models.ReqProgress = Depends()):
res = models.ResProgress(progress=progress, eta_relative=eta_relative, state=shared.state.dict(), current_image=current_image, textinfo=shared.state.textinfo)
return res
def get_status():
return shared.state.status()
def post_interrupt():
shared.state.interrupt()
return {}
-1
View File
@@ -73,7 +73,6 @@ def progressapi(req: ProgressRequest):
elapsed = time.time() - shared.state.time_start if shared.state.time_start is not None else 0
predicted = elapsed / progress if progress > 0 else None
eta = predicted - elapsed if predicted is not None else None
# shared.log.debug(f'Progress: step={step_x}:{step_y} batch={batch_x}:{batch_y} current={current} total={total} progress={progress} elapsed={elapsed} eta={eta}')
id_live_preview = req.id_live_preview
live_preview = None
shared.state.set_current_image()
+2
View File
@@ -1,3 +1,4 @@
from functools import lru_cache
import io
import os
import sys
@@ -1271,6 +1272,7 @@ def html(filename):
return ""
@lru_cache(maxsize=1)
def get_version():
version = None
if version is None:
+32
View File
@@ -62,6 +62,38 @@ class State:
}
return obj
def status(self):
from modules import progress
from modules.api import models
res = models.ResStatus(
task=self.job,
id=progress.current_task or '',
job=max(self.job_no, 0),
jobs=max(self.frame_count, self.job_count, self.job_no),
total=self.total_jobs,
timestamp=self.job_timestamp if self.job != '' else None,
step=self.sampling_step,
steps=self.sampling_steps,
queued=len(progress.pending_tasks),
status='unknown',
uptime = round(time.time() - self.server_start)
)
res.step = res.steps * res.job + res.step
res.steps = res.steps * res.jobs
res.progress = round(min(1, abs(res.step / res.steps) if res.steps > 0 else 0), 2)
res.elapsed = round(time.time() - self.time_start, 2) if self.time_start is not None else None
predicted = round(res.elapsed / res.progress, 2) if res.progress > 0 and res.elapsed is not None else None
res.eta = round(predicted - res.elapsed, 2) if predicted is not None else None
if self.paused:
res.status = 'paused'
elif self.interrupted:
res.status = 'interrupted'
elif self.skipped:
res.status = 'skipped'
else:
res.status = 'running' if self.job != '' else 'idle'
return res
def begin(self, title="", api=None):
import modules.devices
self.total_jobs += 1