[Benchmark] Route i2i/ti2i to POST /v1/images/edits in diffusion_benchmark_serving (#3693)

Signed-off-by: NumberWan <wantszkin2003@gmail.com>
This commit is contained in:
NumberWan authored and GitHub committed 2026-05-21 17:18:02 +08:00
1 parent 7d9d8aee79
commit 474710dda9
2 files changed
+124 -9

No files matched your search

@@ -7,10 +7,13 @@ Benchmark online serving for diffusion models (Image/Video Generation).
If you want to use i2v, i2i dataset, you should `uv pip install gdown` first
Supports multiple endpoints:
- /v1/chat/completions: OpenAI chat-compatible image requests
- /v1/chat/completions: OpenAI chat-compatible image requests (e.g. t2i, Qwen i2i)
- /v1/images/edits: OpenAI image edit / IT2I (multipart; e.g. Hunyuan --bot-task think)
- /v1/images/generations: OpenAI image generation requests
- /v1/videos: Async video jobs
Legacy --backend vllm-omni and openai are aliases for chat/completions and images/generations.
Usage:
# Video (/v1/videos endpoint)
t2v:
@@ -39,7 +42,12 @@ Usage:
{"width":1536,"height":1536,"num_inference_steps":35,"weight":0.15}
]'
i2i:
ti2i (Hunyuan / OpenAI image edit API):
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--endpoint /v1/images/edits --dataset random --task ti2i --num-prompts 10 \
--bot-task think
i2i (chat-based models such as Qwen-Image-Edit):
python3 benchmarks/diffusion/diffusion_benchmark_serving.py \
--endpoint /v1/chat/completions --dataset vbench --task i2i --num-prompts 10
@@ -132,7 +140,7 @@ class VBenchDataset(BaseDataset):
def _load_data(self) -> list[dict[str, Any]]:
if self.args.task == "t2v":
return self._load_t2v_prompts()
elif self.args.task in ["i2v", "ti2v", "ti2i", "i2i"]:
elif self.args.task in ["i2v", "ti2v", "ti2i", "i2i", "it2i"]:
return self._load_i2v_data()
else:
return self._load_t2v_prompts()
@@ -524,7 +532,7 @@ class TraceDataset(BaseDataset):
single = row.get("image_path")
image_paths = [single] if single else None
if not image_paths and self.args.task in ["i2v", "i2i", "ti2v", "ti2i"]:
if not image_paths and self.args.task in ["i2v", "i2i", "ti2v", "ti2i", "it2i"]:
raise ValueError(
f"Task {self.args.task} requires image input, but no image_path or image_paths found in trace row."
)
@@ -585,7 +593,7 @@ class RandomDataset(BaseDataset):
self._sampled_requests = None
# Random image generate
if self.args.task in ["i2v", "ti2v", "ti2i", "i2i"]:
if self.args.task in ["i2v", "ti2v", "ti2i", "i2i", "it2i"]:
self._random_image_path = self._generate_random_image_paths()
else:
self._random_image_path = None
@@ -891,7 +899,9 @@ def wait_for_service(base_url: str, timeout: int = 120) -> None:
def _default_endpoint_for_task(task: str) -> str:
if task in {"t2v", "i2v", "ti2v"}:
return "/v1/videos"
if task in {"t2i", "i2i", "ti2i"}:
if task in {"i2i", "ti2i", "it2i"}:
return "/v1/images/edits"
if task == "t2i":
return "/v1/chat/completions"
raise ValueError(f"Unsupported task for endpoint resolution: {task}")
@@ -902,7 +912,7 @@ async def benchmark(args):
args.base_url = f"http://{args.host}:{args.port}"
VIDEO_TASKS = {"t2v", "i2v", "ti2v"}
IMAGE_TASKS = {"t2i", "i2i", "ti2i"}
IMAGE_TASKS = {"t2i", "i2i", "ti2i", "it2i"}
if args.task in VIDEO_TASKS:
task_type = "2v"
@@ -947,6 +957,10 @@ async def benchmark(args):
requests_list = dataset.get_requests()
print(f"Prepared {len(requests_list)} requests from {args.dataset} dataset.")
if args.endpoint == "/v1/images/edits":
for req in requests_list:
req.default_bot_task = args.bot_task
# Limit concurrency
if args.max_concurrency is not None:
semaphore = asyncio.Semaphore(args.max_concurrency)
@@ -998,6 +1012,8 @@ async def benchmark(args):
metrics["model"] = args.model
metrics["dataset"] = args.dataset
metrics["task"] = args.task
if args.endpoint == "/v1/images/edits":
metrics["bot_task"] = args.bot_task
print("\n{s:{c}^{n}}".format(s=" Serving Benchmark Result ", n=50, c="="))
@@ -1088,7 +1104,7 @@ if __name__ == "__main__":
"--task",
type=str,
default="t2v",
choices=["t2v", "i2v", "ti2v", "ti2i", "i2i", "t2i"],
choices=["t2v", "i2v", "ti2v", "ti2i", "i2i", "it2i", "t2i"],
help="Task type.",
)
parser.add_argument(
@@ -1195,9 +1211,15 @@ if __name__ == "__main__":
default=1,
help=(
"Number of synthetic input images to attach for image-conditioned tasks "
"(i2v, ti2v, ti2i, i2i) when using random dataset."
"(i2v, ti2v, ti2i, i2i, it2i) when using random dataset."
),
)
parser.add_argument(
"--bot-task",
type=str,
default="think",
help=("bot_task form field for --endpoint /v1/images/edits (think, recaption, think_recaption, vanilla)."),
)
args = parser.parse_args()
asyncio.run(benchmark(args))