Spaces:
Running on Zero
Running on Zero
ImageStudio Maintainer Claude Opus 4.8 (1M context) commited on
Commit ·
c29b387
1
Parent(s): 657da31
fix(progress): stream per-step progress for anima image models
Browse filesThe default Anime/Realistic models use the anima family (Cosmos-Predict2
modular pipeline), whose __call__ accepts no callback_on_step_end — so the
diffusers-style step callback never fired and generate_image yielded no
per-step frames. The web progress bar sat at 0% until the final upload.
Wrap the modular pipeline's scheduler.step (called once per denoise step)
to drive the same callback the queue-fed generator already reads, via a
_anima_step_progress context manager that restores the original method in a
finally (no leak across requests) and no-ops if scheduler/callback is absent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import inspect
|
| 2 |
import io
|
| 3 |
import json
|
|
@@ -933,6 +934,47 @@ def _supports_step_callback(pipe):
|
|
| 933 |
return False
|
| 934 |
|
| 935 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 936 |
@spaces.GPU
|
| 937 |
def generate_image(
|
| 938 |
model_name,
|
|
@@ -1134,17 +1176,27 @@ def _generate_image_inner(
|
|
| 1134 |
generator = torch.Generator("cuda").manual_seed(seed)
|
| 1135 |
prompt = _apply_prefix(prompt, entry.get("prefix", ""))
|
| 1136 |
negative_prompt = _resolve_negative(entry, negative_prompt, use_negative_prompt, model_name)
|
| 1137 |
-
|
| 1138 |
-
|
| 1139 |
-
|
| 1140 |
-
|
| 1141 |
-
|
| 1142 |
-
|
| 1143 |
-
|
| 1144 |
-
|
| 1145 |
-
|
| 1146 |
-
|
| 1147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1148 |
image = images[0] if isinstance(images, (list, tuple)) else images
|
| 1149 |
return image, seed
|
| 1150 |
|
|
|
|
| 1 |
+
import contextlib
|
| 2 |
import inspect
|
| 3 |
import io
|
| 4 |
import json
|
|
|
|
| 934 |
return False
|
| 935 |
|
| 936 |
|
| 937 |
+
@contextlib.contextmanager
|
| 938 |
+
def _anima_step_progress(pipe, callback, total_steps):
|
| 939 |
+
"""Emit per-step progress for the modular Anima pipeline by wrapping its
|
| 940 |
+
scheduler's ``step``.
|
| 941 |
+
|
| 942 |
+
The modular (Cosmos-Predict2) pipeline's ``__call__`` doesn't accept
|
| 943 |
+
``callback_on_step_end``, so the diffusers-style ``callback`` the rest of the
|
| 944 |
+
generator relies on never fires for the anima family. The denoise loop calls
|
| 945 |
+
``scheduler.step`` exactly once per inference step, so wrapping it gives a
|
| 946 |
+
reliable per-step tick. We forward into the existing ``callback`` (shape:
|
| 947 |
+
``callback(pipe, step, timestep, callback_kwargs)`` — only the 0-based
|
| 948 |
+
``step`` is used downstream). The original method is always restored, so a
|
| 949 |
+
no-op (missing scheduler / callback) or an exception can't leave the shared
|
| 950 |
+
pipeline patched across requests.
|
| 951 |
+
"""
|
| 952 |
+
sched = getattr(pipe, "scheduler", None)
|
| 953 |
+
orig = getattr(sched, "step", None) if sched is not None else None
|
| 954 |
+
if callback is None or orig is None:
|
| 955 |
+
yield
|
| 956 |
+
return
|
| 957 |
+
|
| 958 |
+
state = {"i": 0}
|
| 959 |
+
|
| 960 |
+
def _counting_step(*args, **kwargs):
|
| 961 |
+
out = orig(*args, **kwargs)
|
| 962 |
+
i = state["i"]
|
| 963 |
+
if i < total_steps:
|
| 964 |
+
state["i"] = i + 1
|
| 965 |
+
try:
|
| 966 |
+
callback(pipe, i, None, {})
|
| 967 |
+
except Exception: # noqa: BLE001 - progress must never break sampling
|
| 968 |
+
pass
|
| 969 |
+
return out
|
| 970 |
+
|
| 971 |
+
sched.step = _counting_step
|
| 972 |
+
try:
|
| 973 |
+
yield
|
| 974 |
+
finally:
|
| 975 |
+
sched.step = orig
|
| 976 |
+
|
| 977 |
+
|
| 978 |
@spaces.GPU
|
| 979 |
def generate_image(
|
| 980 |
model_name,
|
|
|
|
| 1176 |
generator = torch.Generator("cuda").manual_seed(seed)
|
| 1177 |
prompt = _apply_prefix(prompt, entry.get("prefix", ""))
|
| 1178 |
negative_prompt = _resolve_negative(entry, negative_prompt, use_negative_prompt, model_name)
|
| 1179 |
+
# Per-step progress: the modular pipeline doesn't accept
|
| 1180 |
+
# `callback_on_step_end`, so we can't wire the callback the way the
|
| 1181 |
+
# illustrious / zimageturbo branches do. Instead wrap the scheduler's
|
| 1182 |
+
# `step` (called exactly once per denoise step) to drive the same
|
| 1183 |
+
# callback the queue-fed generator reads. Without this the anima family
|
| 1184 |
+
# — which backs the *default* Anime/Realistic models — emits no
|
| 1185 |
+
# sampling frames, so the web progress bar sits at 0% until the final
|
| 1186 |
+
# upload. Restored in a finally so a patched method never leaks across
|
| 1187 |
+
# requests sharing this pipeline.
|
| 1188 |
+
with _anima_step_progress(pipe, callback, int(num_inference_steps)):
|
| 1189 |
+
images = pipe(
|
| 1190 |
+
prompt=prompt,
|
| 1191 |
+
negative_prompt=negative_prompt or None,
|
| 1192 |
+
height=int(height),
|
| 1193 |
+
width=int(width),
|
| 1194 |
+
num_inference_steps=int(num_inference_steps),
|
| 1195 |
+
num_images_per_prompt=1,
|
| 1196 |
+
generator=generator,
|
| 1197 |
+
output_type="pil",
|
| 1198 |
+
output="images",
|
| 1199 |
+
)
|
| 1200 |
image = images[0] if isinstance(images, (list, tuple)) else images
|
| 1201 |
return image, seed
|
| 1202 |
|