Managed ComfyUI: COMFY_CMD starts it on demand, idle stop frees VRAM (internal/supervise)
This commit is contained in:
@@ -26,6 +26,7 @@ import (
|
||||
"gpu-turnstile/internal/lock"
|
||||
"gpu-turnstile/internal/metrics"
|
||||
"gpu-turnstile/internal/ollama"
|
||||
"gpu-turnstile/internal/supervise"
|
||||
)
|
||||
|
||||
// defaultCaptureLimit bounds how much of a /prompt response body is
|
||||
@@ -44,6 +45,11 @@ type Config struct {
|
||||
Metrics *metrics.Metrics
|
||||
Log *slog.Logger
|
||||
|
||||
// ComfySup, when non-nil, is the managed ComfyUI process: any ComfyUI
|
||||
// request starts it on demand, /prompt additionally waits for
|
||||
// readiness before taking the GPU lock. nil = unmanaged upstream.
|
||||
ComfySup *supervise.Process
|
||||
|
||||
// LogColor enables ANSI colors in per-request log lines. Ignored when
|
||||
// the log level is above INFO (request lines are not emitted at all).
|
||||
LogColor bool
|
||||
@@ -500,6 +506,14 @@ func (s *Server) ComfyHandler() http.Handler {
|
||||
s.handlePrompt(w, r)
|
||||
return
|
||||
}
|
||||
if s.cfg.ComfySup != nil {
|
||||
// Any other ComfyUI request also wakes the managed server; the
|
||||
// retry backoff bridges the time it needs to come up.
|
||||
if err := s.cfg.ComfySup.EnsureRunning(); err != nil {
|
||||
http.Error(w, fmt.Sprintf("cannot start ComfyUI: %v", err), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
}
|
||||
s.comfyProxy.ServeHTTP(w, r)
|
||||
}))
|
||||
}
|
||||
@@ -539,6 +553,20 @@ func (w *captureWriter) Unwrap() http.ResponseWriter { return w.ResponseWriter }
|
||||
func (s *Server) handlePrompt(w http.ResponseWriter, r *http.Request) {
|
||||
log := s.log.With("op", "image")
|
||||
|
||||
if s.cfg.ComfySup != nil {
|
||||
// Bring the managed server up *before* taking the GPU lock: torch
|
||||
// can take a minute to load, and LLM traffic should keep flowing
|
||||
// in the meantime.
|
||||
if err := s.cfg.ComfySup.EnsureRunning(); err != nil {
|
||||
http.Error(w, fmt.Sprintf("cannot start ComfyUI: %v", err), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
if err := s.cfg.ComfySup.WaitReady(r.Context()); err != nil {
|
||||
http.Error(w, fmt.Sprintf("ComfyUI did not become ready: %v", err), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
if err := s.cfg.Lock.AcquireImage(r.Context()); err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
@@ -608,6 +636,10 @@ func (s *Server) finishImageJob(promptID string) {
|
||||
|
||||
s.cfg.Lock.ReleaseImage()
|
||||
log.Info("image lock released")
|
||||
if s.cfg.ComfySup != nil {
|
||||
// The idle clock starts when the job ends, not when it began.
|
||||
s.cfg.ComfySup.NoteActivity()
|
||||
}
|
||||
|
||||
if s.cfg.Ollama != nil && s.cfg.WarmModel != "" {
|
||||
if state, _, _ := s.cfg.Lock.Snapshot(); state == lock.StateIdle {
|
||||
|
||||
Reference in New Issue
Block a user