From b840d6aaebd938d3a65f59cf1a787aab67ddd79b Mon Sep 17 00:00:00 2001 From: Li Jinliang <975761915@qq.com> Date: Mon, 29 Dec 2025 10:22:21 +0800 Subject: [PATCH] [diffusion] webui: reference to content task and better visualization capabilities (#16017) --- .../multimodal_gen/apps/webui/README.md | 31 +++---- .../sglang/multimodal_gen/apps/webui/main.py | 84 +++++++++++++++---- 2 files changed, 83 insertions(+), 32 deletions(-) diff --git a/python/sglang/multimodal_gen/apps/webui/README.md b/python/sglang/multimodal_gen/apps/webui/README.md index 104070af8..6b0e89816 100644 --- a/python/sglang/multimodal_gen/apps/webui/README.md +++ b/python/sglang/multimodal_gen/apps/webui/README.md @@ -18,23 +18,23 @@ SGLang Diffusion now includes an integrated WebUI. Simply add the `--webui` para ### Launch Text-to-Image Service ```bash -SERVER_ARGS=( - --model-path black-forest-labs/FLUX.1-dev - --num-gpus 2 -) -WEBUI_PORT=2333 -sglang serve "${SERVER_ARGS[@]}" --webui --webui-port ${WEBUI_PORT} +sglang serve black-forest-labs/FLUX.1-dev --num-gpus 1 --webui --webui-port 2333 ``` ### Launch Text-to-Video Service ```bash -SERVER_ARGS=( - --model-path Wan-AI/Wan2.2-T2V-A14B-Diffusers - --num-gpus 2 -) -WEBUI_PORT=2333 -sglang serve "${SERVER_ARGS[@]}" --webui --webui-port ${WEBUI_PORT} +sglang serve Wan-AI/Wan2.2-T2V-A14B-Diffusers --num-gpus 1 --webui --webui-port 2333 +``` + +### Launch Image-to-Image Service +```bash +sglang serve --model-path Qwen/Qwen-Image-Edit-2511 --num-gpus 1 --webui --webui-port 2333 +``` + +### Launch Image-to-Video Service +```bash +sglang serve Wan-AI/Wan2.2-TI2V-5B-Diffusers --num-gpus 1 --webui --webui-port 2333 ``` ## Port Forwarding @@ -53,11 +53,6 @@ Learn more about port forwarding: [Port Forwarding](https://en.wikipedia.org/wik ## Interface Instructions -1. Task mode is automatically determined by the `num_frames` parameter: - - num_frames = 1: Text-to-Image mode - - num_frames > 1: Text-to-Video mode -2. After generation, manually click: - - Image output: View generated images - - Video output: View generated videos +You can view your model path and task name directly in the UI. We'd appreciate any feedback you'd like to share. Once launched, access the interface at `http://localhost:${WEBUI_PORT}` in your browser. diff --git a/python/sglang/multimodal_gen/apps/webui/main.py b/python/sglang/multimodal_gen/apps/webui/main.py index 1245a2683..cc7a2b6f3 100644 --- a/python/sglang/multimodal_gen/apps/webui/main.py +++ b/python/sglang/multimodal_gen/apps/webui/main.py @@ -11,6 +11,9 @@ from sglang.multimodal_gen.runtime.entrypoints.utils import ( ) from sglang.multimodal_gen.runtime.scheduler_client import sync_scheduler_client from sglang.multimodal_gen.runtime.server_args import ServerArgs +from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger + +logger = init_logger(__name__) def add_webui_args(parser: argparse.ArgumentParser): @@ -22,15 +25,31 @@ def add_webui_args(parser: argparse.ArgumentParser): def run_sgl_diffusion_webui(server_args: ServerArgs): # import gradio in function to avoid CI crash + import gradio as gr + from huggingface_hub import model_info # init client sync_scheduler_client.initialize(server_args) + task_name = model_info(server_args.model_path).pipeline_tag + + if task_name in ("text-to-video", "image-to-video", "video-to-video"): + task_type = "video" + elif task_name in ["text-to-image", "image-to-image"]: + task_type = "image" + else: + raise ValueError( + f"The task name {task_name} of model {server_args.model_path} is not a valid task name. Please check the model path." + ) + video_visible_only = task_type == "video" + image_visible_only = task_type == "image" + # server_args will be reused in gradio_generate function def gradio_generate( prompt, negative_prompt, + reference_image_paths_str, seed, num_frames, frames_per_second, @@ -45,9 +64,20 @@ def run_sgl_diffusion_webui(server_args: ServerArgs): So we use global variable sampling_params_kwargs to avoid pass this param, because gradio does not support this. return [ np.ndarray, None ] | [None, np.ndarray] """ + if reference_image_paths_str: + if "," in reference_image_paths_str: + logger.warning( + f"Warning: please use English comma to separate the reference image paths, and the reference image paths is: {reference_image_paths_str}" + ) + reference_image_paths_str = reference_image_paths_str.replace(",", ",") + image_path = [path.strip() for path in reference_image_paths_str.split(",")] + else: + image_path = None + sampling_params_kwargs = dict( prompt=prompt, negative_prompt=negative_prompt, + image_path=image_path, seed=seed, num_frames=num_frames, fps=frames_per_second, @@ -68,6 +98,14 @@ def run_sgl_diffusion_webui(server_args: ServerArgs): ) result = sync_scheduler_client.forward([batch]) save_file_path = str(os.path.join(batch.output_path, batch.output_file_name)) + if result.output is None: + sampling_params_str = "\n".join( + [f"{key}: {value}" for key, value in sampling_params_kwargs.items()] + ) + raise ValueError( + f"No output is generated by client, and their sampling params is: {sampling_params_str}" + ) + frames = post_process_sample( result.output[0], batch.data_type, @@ -83,7 +121,9 @@ def run_sgl_diffusion_webui(server_args: ServerArgs): with gr.Blocks() as demo: gr.Markdown("# 🚀 SGLang Diffusion Application") - launched_model = gr.Textbox(label="Model", value=server_args.model_path) + with gr.Row(): + launched_model_box = gr.Textbox(label="Model", value=server_args.model_path) + task_name_box = gr.Textbox(label="Task name", value=task_name) with gr.Row(): with gr.Column(scale=4): @@ -98,12 +138,6 @@ def run_sgl_diffusion_webui(server_args: ServerArgs): with gr.Row(): with gr.Column(): - num_frames = gr.Slider( - minimum=1, maximum=161, value=81, step=1, label="num_frames" - ) - frames_per_second = gr.Slider( - minimum=4, maximum=60, value=16, step=1, label="frames_per_second" - ) width = gr.Number(label="width", precision=0, value=720) height = gr.Number(label="height", precision=0, value=480) num_inference_steps = gr.Slider( @@ -112,21 +146,42 @@ def run_sgl_diffusion_webui(server_args: ServerArgs): guidance_scale = gr.Slider( minimum=0.0, maximum=10, value=5, step=0.01, label="guidance_scale" ) + num_frames = gr.Slider( + minimum=1, + maximum=181, + value=81, + step=1, + label="num_frames", + visible=video_visible_only, + ) + frames_per_second = gr.Slider( + minimum=4, + maximum=60, + value=16, + step=1, + label="frames_per_second", + visible=video_visible_only, + ) + reference_image_paths_str = gr.Textbox( + label="reference images", + placeholder="Examples: 'image1.png, image2.png' or 'https://example.com/image1.png, https://example.com/image2.png'", + ) enable_teacache = gr.Checkbox(label="enable_teacache", value=False) - with gr.Tabs() as tabs: - with gr.TabItem("Image output", id=1): - image_out = gr.Image( - label="Generated Image", - ) - with gr.TabItem("Video output", id=2): - video_out = gr.Video(label="Generated Video") + with gr.Column(): + image_out = gr.Image( + label="Generated Image", visible=image_visible_only + ) + video_out = gr.Video( + label="Generated Video", visible=video_visible_only + ) run_btn.click( fn=gradio_generate, inputs=[ prompt, negative_prompt, + reference_image_paths_str, seed, num_frames, frames_per_second, @@ -143,6 +198,7 @@ def run_sgl_diffusion_webui(server_args: ServerArgs): server_port=server_args.webui_port, quiet=True, prevent_thread_lock=True, + show_error=True, ) # print banner