109 lines
3.1 KiB
Python
109 lines
3.1 KiB
Python
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
|
# Copyright (C) 2026 Artificial Sweetener and contributors
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
"""Small progress reporting boundary for ComfyUI node work."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from importlib import import_module
|
|
from typing import Protocol, cast
|
|
|
|
from ..shared.logging import get_logger
|
|
|
|
LOGGER = get_logger(__name__)
|
|
|
|
|
|
class ProgressReporter(Protocol):
|
|
"""Progress sink compatible with ComfyUI progress bars."""
|
|
|
|
def update(self, value: int) -> None:
|
|
"""Advance progress by value."""
|
|
|
|
|
|
class NullProgressReporter:
|
|
"""Ignore progress updates when no UI progress sink is available."""
|
|
|
|
def update(self, value: int) -> None:
|
|
"""Ignore progress updates."""
|
|
|
|
|
|
class PhaseProgressReporter(Protocol):
|
|
"""Report named phases for a bounded long-running operation."""
|
|
|
|
def advance(self, phase: str) -> None:
|
|
"""Report that the operation entered ``phase``."""
|
|
|
|
|
|
class NullPhaseProgressReporter:
|
|
"""Ignore named phase updates outside a Comfy execution context."""
|
|
|
|
def advance(self, phase: str) -> None:
|
|
"""Ignore one phase update."""
|
|
|
|
|
|
@dataclass
|
|
class ComfyPhaseProgressReporter:
|
|
"""Publish truthful phase boundaries through standard Comfy progress events."""
|
|
|
|
operation: str
|
|
subject: str
|
|
total_phases: int
|
|
|
|
def __post_init__(self) -> None:
|
|
"""Create a lazily imported Comfy progress bar and initialize state."""
|
|
|
|
if self.total_phases <= 0:
|
|
raise ValueError("total_phases must be positive.")
|
|
comfy_utils = import_module("comfy.utils")
|
|
self._progress_bar = comfy_utils.ProgressBar(self.total_phases)
|
|
self._completed_phases = 0
|
|
|
|
def advance(self, phase: str) -> None:
|
|
"""Publish one phase transition without inventing intra-phase percentages."""
|
|
|
|
self._completed_phases = (
|
|
self.total_phases
|
|
if phase == "completed"
|
|
else min(self.total_phases - 1, self._completed_phases + 1)
|
|
)
|
|
self._progress_bar.update_absolute(
|
|
self._completed_phases,
|
|
self.total_phases,
|
|
)
|
|
LOGGER.info(
|
|
"Long operation entered phase",
|
|
extra={
|
|
"operation": self.operation,
|
|
"subject": self.subject,
|
|
"phase": phase,
|
|
"completed_phases": self._completed_phases,
|
|
"total_phases": self.total_phases,
|
|
},
|
|
)
|
|
|
|
|
|
def create_comfy_progress(total: int) -> ProgressReporter:
|
|
"""Return a ComfyUI progress bar for total units of work."""
|
|
|
|
if total <= 0:
|
|
return NullProgressReporter()
|
|
comfy_utils = import_module("comfy.utils")
|
|
return cast(ProgressReporter, comfy_utils.ProgressBar(total))
|
|
|
|
|
|
def create_comfy_phase_progress(
|
|
*,
|
|
operation: str,
|
|
subject: str,
|
|
total_phases: int,
|
|
) -> PhaseProgressReporter:
|
|
"""Return a standard Comfy progress publisher for named phases."""
|
|
|
|
return ComfyPhaseProgressReporter(
|
|
operation=operation,
|
|
subject=subject,
|
|
total_phases=total_phases,
|
|
)
|