[Feature] Add trajectory recording to BAGEL denoising loop (#2483)

Signed-off-by: Zhengyuan Su <su.zhengyuan@u.nus.edu>
Signed-off-by: Zhengyuan Su (苏政渊) <su.zhengyuan@u.nus.edu>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Samit <285365963@qq.com>
This commit is contained in:
authored and GitHub committed 2026-04-08 10:43:01 +08:00
1 parent 6433847249
commit cb6a8739c1
9 files changed
+412 -22

No files matched your search

@@ -0,0 +1,236 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Unit tests for BAGEL trajectory recording in the denoising loop."""
import types
from dataclasses import dataclass
from unittest.mock import MagicMock, patch
import pytest
import torch
from vllm_omni.diffusion.models.bagel.bagel_transformer import (
Bagel,
NaiveCache,
)
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
NUM_TOKENS = 8
HIDDEN_DIM = 16
NUM_TIMESTEPS = 5
# generate_image uses timesteps[:-1], so actual steps = NUM_TIMESTEPS - 1
EXPECTED_STEPS = NUM_TIMESTEPS - 1
def _make_mock_bagel():
"""Create a mock Bagel with _forward_flow returning constant velocity."""
mock = MagicMock(spec=Bagel)
mock._sp_size = 1
# _forward_flow returns a small constant velocity so x_t changes each step
def fake_forward_flow(self, x_t, **kwargs):
return torch.ones_like(x_t) * 0.1
mock._forward_flow = types.MethodType(fake_forward_flow, mock)
# _merge_naive_caches is called in the batched CFG path
mock._merge_naive_caches = types.MethodType(lambda self, caches: NaiveCache(1), mock)
# Bind the real generate_image to our mock
mock.generate_image = types.MethodType(Bagel.generate_image, mock)
return mock
def _make_generate_args(num_tokens=NUM_TOKENS, hidden_dim=HIDDEN_DIM, cfg=False):
"""Tensor arguments for generate_image.
Args:
cfg: If True, enable batched CFG path (cfg_text_scale > 1.0).
"""
seq_len = num_tokens + 2 # packed_seqlens includes 2 extra tokens
base = dict(
packed_text_ids=torch.zeros(2, dtype=torch.long),
packed_text_indexes=torch.tensor([0, 1], dtype=torch.long),
packed_init_noises=torch.randn(num_tokens, hidden_dim),
packed_vae_position_ids=torch.arange(num_tokens, dtype=torch.long),
packed_vae_token_indexes=torch.arange(2, seq_len, dtype=torch.long),
packed_seqlens=torch.tensor([seq_len], dtype=torch.int),
packed_position_ids=torch.arange(seq_len, dtype=torch.long),
packed_indexes=torch.arange(seq_len, dtype=torch.long),
past_key_values=NaiveCache(1),
key_values_lens=torch.tensor([0], dtype=torch.int),
packed_key_value_indexes=torch.zeros(0, dtype=torch.long),
num_timesteps=NUM_TIMESTEPS,
timestep_shift=1.0,
cfg_text_scale=1.0,
cfg_img_scale=1.0,
)
if cfg:
base |= dict(
cfg_text_scale=4.0,
cfg_text_packed_query_indexes=torch.arange(seq_len, dtype=torch.long),
cfg_text_packed_position_ids=torch.arange(seq_len, dtype=torch.long),
cfg_text_past_key_values=NaiveCache(1),
cfg_text_key_values_lens=torch.tensor([0], dtype=torch.int),
cfg_text_packed_key_value_indexes=torch.zeros(0, dtype=torch.long),
)
return base
@pytest.fixture(params=[False, True], ids=["no_cfg", "batched_cfg"])
def bagel_and_args(request):
"""Mock Bagel instance and generate_image arguments.
Parametrized over CFG mode so every test runs on both the no-CFG
and batched-CFG code paths.
"""
cfg = request.param
with patch(
"vllm_omni.diffusion.models.bagel.bagel_transformer.get_classifier_free_guidance_world_size",
return_value=1,
):
yield _make_mock_bagel(), _make_generate_args(cfg=cfg)
class TestTrajectoryRecording:
"""Tests for trajectory latent/timestep recording in generate_image."""
def test_trajectory_disabled_returns_none(self, bagel_and_args):
bagel, args = bagel_and_args
unpacked, trajectory_latents, trajectory_timesteps, trajectory_log_probs = bagel.generate_image(
**args, return_trajectory_latents=False
)
assert isinstance(unpacked, (list, tuple))
assert len(unpacked) == 1 # one sequence
assert trajectory_latents is None
assert trajectory_timesteps is None
assert trajectory_log_probs is None
def test_trajectory_enabled_returns_correct_count(self, bagel_and_args):
bagel, args = bagel_and_args
_, trajectory_latents, trajectory_timesteps, trajectory_log_probs = bagel.generate_image(
**args, return_trajectory_latents=True
)
assert trajectory_latents is not None
assert trajectory_timesteps is not None
assert len(trajectory_latents) == EXPECTED_STEPS
assert len(trajectory_timesteps) == EXPECTED_STEPS
# log_probs is None without a scheduler (default ODE path)
assert trajectory_log_probs is None
def test_trajectory_latents_shape_matches_input(self, bagel_and_args):
bagel, args = bagel_and_args
expected_shape = args["packed_init_noises"].shape
_, trajectory_latents, *_ = bagel.generate_image(**args, return_trajectory_latents=True)
for i, lat in enumerate(trajectory_latents):
assert lat.shape == expected_shape, f"Step {i}: expected {expected_shape}, got {lat.shape}"
def test_trajectory_latents_are_distinct(self, bagel_and_args):
bagel, args = bagel_and_args
_, trajectory_latents, *_ = bagel.generate_image(**args, return_trajectory_latents=True)
for i in range(1, len(trajectory_latents)):
assert not torch.equal(trajectory_latents[i], trajectory_latents[i - 1]), (
f"Steps {i - 1} and {i} should differ"
)
def test_trajectory_timesteps_are_decreasing(self, bagel_and_args):
bagel, args = bagel_and_args
_, _, trajectory_timesteps, _ = bagel.generate_image(**args, return_trajectory_latents=True)
for i in range(1, len(trajectory_timesteps)):
assert trajectory_timesteps[i] < trajectory_timesteps[i - 1], (
f"Timestep {i} ({trajectory_timesteps[i]:.4f}) should be less than "
f"timestep {i - 1} ({trajectory_timesteps[i - 1]:.4f})"
)
def test_trajectory_final_latent_matches_output(self, bagel_and_args):
bagel, args = bagel_and_args
unpacked, trajectory_latents, *_ = bagel.generate_image(**args, return_trajectory_latents=True)
# Reconstruct the full final latent from unpacked pieces
final_latent = torch.cat(unpacked, dim=0)
assert torch.allclose(trajectory_latents[-1], final_latent, atol=1e-6), (
"Last trajectory latent should match the final output"
)
# ---------------------------------------------------------------------------
# Mock scheduler for log-prob tests
# ---------------------------------------------------------------------------
@dataclass
class _MockStepOutput:
prev_sample: torch.Tensor
log_prob: torch.Tensor
class _MockScheduler:
"""Minimal scheduler: Euler step + constant log-prob per step."""
def step(self, model_output, sigma, sample, dt, **kwargs):
prev_sample = sample - model_output * dt
log_prob = torch.tensor(-1.0)
return _MockStepOutput(prev_sample=prev_sample, log_prob=log_prob)
class TestTrajectoryLogProbs:
"""Tests for log-prob recording when a scheduler is provided."""
@pytest.fixture()
def bagel_scheduler_args(self):
with patch(
"vllm_omni.diffusion.models.bagel.bagel_transformer.get_classifier_free_guidance_world_size",
return_value=1,
):
yield _make_mock_bagel(), _make_generate_args(), _MockScheduler()
def test_log_probs_recorded_with_scheduler(self, bagel_scheduler_args):
bagel, args, scheduler = bagel_scheduler_args
_, _, _, trajectory_log_probs = bagel.generate_image(
**args, return_trajectory_latents=True, scheduler=scheduler
)
assert trajectory_log_probs is not None
assert len(trajectory_log_probs) == EXPECTED_STEPS
def test_log_probs_are_finite(self, bagel_scheduler_args):
bagel, args, scheduler = bagel_scheduler_args
_, _, _, trajectory_log_probs = bagel.generate_image(
**args, return_trajectory_latents=True, scheduler=scheduler
)
for i, lp in enumerate(trajectory_log_probs):
assert torch.isfinite(lp).all(), f"Step {i}: log_prob is not finite"
def test_log_probs_none_without_scheduler(self, bagel_scheduler_args):
bagel, args, _ = bagel_scheduler_args
_, _, _, trajectory_log_probs = bagel.generate_image(**args, return_trajectory_latents=True, scheduler=None)
assert trajectory_log_probs is None
def test_scheduler_updates_latents(self, bagel_scheduler_args):
"""Verify the scheduler's prev_sample is used (not the raw Euler step)."""
bagel, args, scheduler = bagel_scheduler_args
_, traj_with_sched, *_ = bagel.generate_image(**args, return_trajectory_latents=True, scheduler=scheduler)
_, traj_without, *_ = bagel.generate_image(**args, return_trajectory_latents=True, scheduler=None)
# Mock scheduler does the same Euler step, so latents should match
for i in range(len(traj_with_sched)):
assert torch.allclose(traj_with_sched[i], traj_without[i], atol=1e-5), (
f"Step {i}: scheduler and ODE paths should produce same latents"
)
+7 -4
View File
@@ -9,6 +9,7 @@ from dataclasses import dataclass, field, fields
from typing import TYPE_CHECKING, Any
import torch
from PIL import Image
from pydantic import model_validator
from typing_extensions import Self
from vllm.config.utils import config
@@ -701,10 +702,12 @@ class DiffusionOutput:
Final output (after pipeline completion)
"""
output: torch.Tensor | None = None
trajectory_timesteps: list[torch.Tensor] | None = None
trajectory_latents: torch.Tensor | None = None
trajectory_decoded: list[torch.Tensor] | None = None
# Fields may be replaced with SHM handle dicts by ipc.pack_diffusion_output_shm
output: torch.Tensor | dict | None = None
trajectory_timesteps: torch.Tensor | dict | None = None
trajectory_latents: torch.Tensor | dict | None = None
trajectory_log_probs: torch.Tensor | dict | None = None
trajectory_decoded: list[Image.Image] | None = None
error: str | None = None
aborted: bool = False
abort_message: str | None = None
+8
View File
@@ -209,6 +209,10 @@ class DiffusionEngine:
prompt=prompt,
metrics=metrics,
latents=output.trajectory_latents,
trajectory_latents=output.trajectory_latents,
trajectory_timesteps=output.trajectory_timesteps,
trajectory_log_probs=output.trajectory_log_probs,
trajectory_decoded=output.trajectory_decoded,
custom_output=output.custom_output or {},
multimodal_output=mm_output,
stage_durations=output.stage_durations,
@@ -267,6 +271,10 @@ class DiffusionEngine:
prompt=prompt,
metrics=metrics,
latents=output.trajectory_latents,
trajectory_latents=output.trajectory_latents,
trajectory_timesteps=output.trajectory_timesteps,
trajectory_log_probs=output.trajectory_log_probs,
trajectory_decoded=output.trajectory_decoded,
custom_output=output.custom_output or {},
multimodal_output=mm_output,
stage_durations=output.stage_durations,
+24 -8
View File
@@ -78,13 +78,29 @@ def _tensor_from_shm(handle: dict[str, Any]) -> torch.Tensor:
return tensor
def _pack_tensor_if_large(val: torch.Tensor) -> torch.Tensor | dict:
"""Replace a tensor with an SHM handle if it exceeds the threshold."""
if val.nelement() * val.element_size() > _SHM_TENSOR_THRESHOLD:
return _tensor_to_shm(val)
return val
def _unpack_if_shm_handle(val: object) -> object:
"""Reconstruct a tensor from an SHM handle dict, or return as-is."""
if isinstance(val, dict) and val.get("__tensor_shm__"):
return _tensor_from_shm(val)
return val
def _pack_diffusion_fields(output: DiffusionOutput) -> DiffusionOutput:
if output.output is not None and isinstance(output.output, torch.Tensor):
if output.output.nelement() * output.output.element_size() > _SHM_TENSOR_THRESHOLD:
output.output = _tensor_to_shm(output.output)
output.output = _pack_tensor_if_large(output.output)
if output.trajectory_latents is not None and isinstance(output.trajectory_latents, torch.Tensor):
if output.trajectory_latents.nelement() * output.trajectory_latents.element_size() > _SHM_TENSOR_THRESHOLD:
output.trajectory_latents = _tensor_to_shm(output.trajectory_latents)
output.trajectory_latents = _pack_tensor_if_large(output.trajectory_latents)
if output.trajectory_timesteps is not None and isinstance(output.trajectory_timesteps, torch.Tensor):
output.trajectory_timesteps = _pack_tensor_if_large(output.trajectory_timesteps)
if output.trajectory_log_probs is not None and isinstance(output.trajectory_log_probs, torch.Tensor):
output.trajectory_log_probs = _pack_tensor_if_large(output.trajectory_log_probs)
return output
@@ -104,10 +120,10 @@ def pack_diffusion_output_shm(output: object) -> object:
def _unpack_diffusion_fields(output: DiffusionOutput) -> DiffusionOutput:
if isinstance(output.output, dict) and output.output.get("__tensor_shm__"):
output.output = _tensor_from_shm(output.output)
if isinstance(output.trajectory_latents, dict) and output.trajectory_latents.get("__tensor_shm__"):
output.trajectory_latents = _tensor_from_shm(output.trajectory_latents)
output.output = _unpack_if_shm_handle(output.output)
output.trajectory_latents = _unpack_if_shm_handle(output.trajectory_latents)
output.trajectory_timesteps = _unpack_if_shm_handle(output.trajectory_timesteps)
output.trajectory_log_probs = _unpack_if_shm_handle(output.trajectory_log_probs)
return output
@@ -1655,6 +1655,9 @@ class Bagel(nn.Module):
cfg_img_past_key_values: NaiveCache | None = None,
cfg_img_key_values_lens: torch.IntTensor | None = None,
cfg_img_packed_key_value_indexes: torch.LongTensor | None = None,
return_trajectory_latents: bool = False,
scheduler: object | None = None,
scheduler_kwargs: dict | None = None,
):
x_t = packed_init_noises
@@ -1663,6 +1666,14 @@ class Bagel(nn.Module):
dts = timesteps[:-1] - timesteps[1:]
timesteps = timesteps[:-1]
# Optional trajectory recording for RL rollout data collection
trajectory_latents: list[torch.Tensor] | None = [] if return_trajectory_latents else None
trajectory_timesteps: list[torch.Tensor] | None = [] if return_trajectory_latents else None
trajectory_log_probs: list[torch.Tensor] | None = (
[] if (return_trajectory_latents and scheduler is not None) else None
)
_sched_kw = scheduler_kwargs or {}
use_cfg_text = cfg_text_scale > 1.0
use_cfg_img = cfg_img_scale > 1.0
@@ -1699,6 +1710,9 @@ class Bagel(nn.Module):
cfg_img_past_key_values=cfg_img_past_key_values,
cfg_img_key_values_lens=cfg_img_key_values_lens,
cfg_img_packed_key_value_indexes=cfg_img_packed_key_value_indexes,
return_trajectory_latents=return_trajectory_latents,
scheduler=scheduler,
scheduler_kwargs=scheduler_kwargs,
)
# ── SP + CFG: sequential single-branch forwards ──
@@ -1758,10 +1772,19 @@ class Bagel(nn.Module):
cfg_renorm_min,
)
x_t = x_t - v_t.to(x_t.device) * dts[i]
if scheduler is not None:
out = scheduler.step(v_t.to(x_t.device), timesteps[i], x_t, dts[i], **_sched_kw)
x_t = out.prev_sample
if trajectory_log_probs is not None and out.log_prob is not None:
trajectory_log_probs.append(out.log_prob)
else:
x_t = x_t - v_t.to(x_t.device) * dts[i]
if return_trajectory_latents:
trajectory_latents.append(x_t.clone())
trajectory_timesteps.append(timesteps[i] - dts[i])
unpacked_latent = x_t.split((packed_seqlens - 2).tolist())
return unpacked_latent
return unpacked_latent, trajectory_latents, trajectory_timesteps, trajectory_log_probs
# ── SP without CFG: direct single-branch loop ──
if use_sp:
@@ -1781,10 +1804,20 @@ class Bagel(nn.Module):
past_key_values=past_key_values,
packed_key_value_indexes=packed_key_value_indexes,
)
x_t = x_t - v_t.to(x_t.device) * dts[i]
if scheduler is not None:
out = scheduler.step(v_t.to(x_t.device), timesteps[i], x_t, dts[i], **_sched_kw)
x_t = out.prev_sample
out_log_prob = getattr(out, "log_prob", None)
if trajectory_log_probs is not None and out_log_prob is not None:
trajectory_log_probs.append(out_log_prob)
else:
x_t = x_t - v_t.to(x_t.device) * dts[i]
if return_trajectory_latents:
trajectory_latents.append(x_t.clone())
trajectory_timesteps.append(timesteps[i] - dts[i])
unpacked_latent = x_t.split((packed_seqlens - 2).tolist())
return unpacked_latent
return unpacked_latent, trajectory_latents, trajectory_timesteps, trajectory_log_probs
# ── Batched CFG mode (cfg_parallel_size=1, no SP) ──
cfg_batched = None
@@ -1870,10 +1903,19 @@ class Bagel(nn.Module):
cfg_batched=cfg_batched,
)
x_t = x_t - v_t.to(x_t.device) * dts[i] # velocity pointing from data to noise
if scheduler is not None:
out = scheduler.step(v_t.to(x_t.device), timesteps[i], x_t, dts[i], **_sched_kw)
x_t = out.prev_sample
if trajectory_log_probs is not None and out.log_prob is not None:
trajectory_log_probs.append(out.log_prob)
else:
x_t = x_t - v_t.to(x_t.device) * dts[i] # velocity pointing from data to noise
if return_trajectory_latents:
trajectory_latents.append(x_t.clone())
trajectory_timesteps.append(timesteps[i] - dts[i])
unpacked_latent = x_t.split((packed_seqlens - 2).tolist())
return unpacked_latent
return unpacked_latent, trajectory_latents, trajectory_timesteps, trajectory_log_probs
def _generate_image_parallel(
self,
@@ -1905,6 +1947,9 @@ class Bagel(nn.Module):
cfg_img_past_key_values: NaiveCache | None,
cfg_img_key_values_lens: torch.IntTensor | None,
cfg_img_packed_key_value_indexes: torch.LongTensor | None,
return_trajectory_latents: bool = False,
scheduler: object | None = None,
scheduler_kwargs: dict | None = None,
):
"""CFG parallel denoising loop: each rank computes one CFG branch.
@@ -1961,6 +2006,13 @@ class Bagel(nn.Module):
else:
raise RuntimeError(f"Unexpected cfg_rank={cfg_rank} for Bagel 3-branch CFG parallel")
trajectory_latents: list[torch.Tensor] | None = [] if return_trajectory_latents else None
trajectory_timesteps: list[torch.Tensor] | None = [] if return_trajectory_latents else None
trajectory_log_probs: list[torch.Tensor] | None = (
[] if (return_trajectory_latents and scheduler is not None) else None
)
_sched_kw = scheduler_kwargs or {}
for i, t in enumerate(timesteps):
timestep = torch.tensor([t] * x_t.shape[0], device=x_t.device)
use_cfg_this_step = t > cfg_interval[0] and t <= cfg_interval[1] and cfg_text_scale > 1.0
@@ -2009,10 +2061,19 @@ class Bagel(nn.Module):
packed_key_value_indexes=packed_key_value_indexes,
)
x_t = x_t - v_t.to(x_t.device) * dts[i]
if scheduler is not None:
out = scheduler.step(v_t.to(x_t.device), timesteps[i], x_t, dts[i], **_sched_kw)
x_t = out.prev_sample
if trajectory_log_probs is not None and out.log_prob is not None:
trajectory_log_probs.append(out.log_prob)
else:
x_t = x_t - v_t.to(x_t.device) * dts[i]
if return_trajectory_latents:
trajectory_latents.append(x_t.clone())
trajectory_timesteps.append(timesteps[i] - dts[i])
unpacked_latent = x_t.split((packed_seqlens - 2).tolist())
return unpacked_latent
return unpacked_latent, trajectory_latents, trajectory_timesteps, trajectory_log_probs
@staticmethod
def _combine_cfg(
@@ -159,6 +159,9 @@ class BagelPipeline(nn.Module, DiffusionPipelineProfilerMixin):
self.od_config = od_config
self.device = get_local_device()
self._scheduler: object | None = None
self._scheduler_kwargs: dict = {}
model = od_config.model
local_files_only = os.path.exists(model)
if local_files_only:
@@ -630,7 +633,7 @@ class BagelPipeline(nn.Module, DiffusionPipelineProfilerMixin):
enabled=self.device.type != "cpu",
dtype=self.od_config.dtype,
):
latents = self.bagel.generate_image(
latents, trajectory_latents, trajectory_timesteps, trajectory_log_probs = self.bagel.generate_image(
past_key_values=gen_context["past_key_values"],
cfg_text_past_key_values=cfg_text_context["past_key_values"],
cfg_img_past_key_values=cfg_img_context["past_key_values"],
@@ -650,11 +653,36 @@ class BagelPipeline(nn.Module, DiffusionPipelineProfilerMixin):
cfg_img_packed_query_indexes=generation_input_cfg_img["cfg_packed_query_indexes"],
cfg_img_key_values_lens=generation_input_cfg_img["cfg_key_values_lens"],
cfg_img_packed_key_value_indexes=generation_input_cfg_img["cfg_packed_key_value_indexes"],
return_trajectory_latents=req.sampling_params.return_trajectory_latents,
scheduler=self._scheduler,
scheduler_kwargs=self._scheduler_kwargs,
)
img = self._decode_image_from_latent(self.bagel, self.vae, latents[0], image_shape)
# Build trajectory output when requested
trajectory_latents_stacked: torch.Tensor | None = None
trajectory_timesteps_stacked: torch.Tensor | None = None
trajectory_decoded: list[Image.Image] | None = None
if trajectory_latents:
trajectory_latents_stacked = torch.stack(trajectory_latents)
trajectory_timesteps_stacked = torch.stack(trajectory_timesteps)
if req.sampling_params.return_trajectory_decoded:
trajectory_decoded = [
self._decode_image_from_latent(self.bagel, self.vae, lat, image_shape) for lat in trajectory_latents
]
trajectory_log_probs_stacked: torch.Tensor | None = None
if trajectory_log_probs:
trajectory_log_probs_stacked = torch.stack(trajectory_log_probs)
return DiffusionOutput(
output=img, stage_durations=self.stage_durations if hasattr(self, "stage_durations") else None
output=img,
trajectory_latents=trajectory_latents_stacked,
trajectory_timesteps=trajectory_timesteps_stacked,
trajectory_log_probs=trajectory_log_probs_stacked,
trajectory_decoded=trajectory_decoded,
stage_durations=self.stage_durations if hasattr(self, "stage_durations") else None,
)
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
@@ -14,8 +14,10 @@ from multiprocessing.process import BaseProcess
from typing import TYPE_CHECKING, Any
import msgspec
import torch
import zmq
import zmq.asyncio
from PIL import Image
from vllm.logger import init_logger
from vllm.transformers_utils.config import get_hf_file_to_dict
from vllm.utils.network_utils import get_open_zmq_ipc_path, zmq_socket_ctx
@@ -174,8 +176,13 @@ class StageDiffusionProc:
merged_mm: dict[str, Any] = {}
merged_metrics: dict[str, Any] = {}
merged_durations: dict[str, float] = {}
merged_custom: dict[str, Any] = {}
peak_mem = 0.0
latents = None
trajectory_latents: list[torch.Tensor] | None = None
trajectory_timesteps: list[torch.Tensor] | None = None
trajectory_log_probs: torch.Tensor | None = None
trajectory_decoded: list[Image.Image] | None = None
final_output_type = "image"
for r in results:
@@ -183,9 +190,18 @@ class StageDiffusionProc:
merged_mm.update(r._multimodal_output)
merged_metrics.update(r.metrics)
merged_durations.update(r.stage_durations)
merged_custom.update(r._custom_output)
peak_mem = max(peak_mem, r.peak_memory_mb)
if latents is None and r.latents is not None:
latents = r.latents
if trajectory_latents is None:
trajectory_latents = r.trajectory_latents
if trajectory_timesteps is None:
trajectory_timesteps = r.trajectory_timesteps
if trajectory_log_probs is None:
trajectory_log_probs = r.trajectory_log_probs
if trajectory_decoded is None:
trajectory_decoded = r.trajectory_decoded
if r.final_output_type != "image":
final_output_type = r.final_output_type
@@ -195,6 +211,11 @@ class StageDiffusionProc:
prompt=prompts[0] if len(prompts) == 1 else None,
metrics=merged_metrics,
latents=latents,
trajectory_latents=trajectory_latents,
trajectory_timesteps=trajectory_timesteps,
trajectory_log_probs=trajectory_log_probs,
trajectory_decoded=trajectory_decoded,
custom_output=merged_custom or None,
multimodal_output=merged_mm or None,
final_output_type=final_output_type,
stage_durations=merged_durations,
+5
View File
@@ -282,6 +282,11 @@ class OmniBase:
final_output_type=stage_meta["final_output_type"],
request_output=engine_outputs,
images=images,
trajectory_latents=getattr(engine_outputs, "trajectory_latents", None),
trajectory_timesteps=getattr(engine_outputs, "trajectory_timesteps", None),
trajectory_log_probs=getattr(engine_outputs, "trajectory_log_probs", None),
trajectory_decoded=getattr(engine_outputs, "trajectory_decoded", None),
_custom_output=getattr(engine_outputs, "_custom_output", {}),
stage_durations=stage_durations,
peak_memory_mb=peak_memory_mb,
)
+12
View File
@@ -58,6 +58,10 @@ class OmniRequestOutput:
images: list[Image.Image] = field(default_factory=list)
prompt: OmniPromptType | None = None
latents: torch.Tensor | None = None
trajectory_latents: torch.Tensor | None = None
trajectory_timesteps: torch.Tensor | None = None
trajectory_log_probs: torch.Tensor | None = None
trajectory_decoded: list | None = None
metrics: dict[str, Any] = field(default_factory=dict)
_multimodal_output: dict[str, Any] = field(default_factory=dict)
_custom_output: dict[str, Any] = field(default_factory=dict)
@@ -101,6 +105,10 @@ class OmniRequestOutput:
prompt: OmniPromptType | None = None,
metrics: dict[str, Any] | None = None,
latents: torch.Tensor | None = None,
trajectory_latents: torch.Tensor | None = None,
trajectory_timesteps: torch.Tensor | None = None,
trajectory_log_probs: torch.Tensor | None = None,
trajectory_decoded: list | None = None,
multimodal_output: dict[str, Any] | None = None,
custom_output: dict[str, Any] | None = None,
final_output_type: str = "image",
@@ -129,6 +137,10 @@ class OmniRequestOutput:
images=images,
prompt=prompt,
latents=latents,
trajectory_latents=trajectory_latents,
trajectory_timesteps=trajectory_timesteps,
trajectory_log_probs=trajectory_log_probs,
trajectory_decoded=trajectory_decoded,
metrics=metrics or {},
_multimodal_output=multimodal_output or {},
_custom_output=custom_output or {},