Files
lishunyang12 7c934b6188 [Lance] Add Gradio demo and align inference with upstream
- Add ``examples/offline_inference/lance/gradio_demo.py`` — a unified 7-task UI
  (t2i / t2v / i2v / image_edit / video_edit / x2t_image / x2t_video) that
  loads ``Lance_3B`` and ``Lance_3B_Video`` on two GPUs so image and video
  tasks run concurrently.  Surfaces official Lance examples per task; aspect
  ratio is auto-snapped to one of upstream's 6 buckets from the input
  image/video.
- Fix x2t_image / x2t_video output format: route x2t_image through a new
  ``_forward_x2t_image`` handler (was falling through to the img2img path
  and emitting garbage), build the chat prefill in three segments
  (prefix → ViT → suffix) so the ``<|im_end|>`` markers line up with
  upstream's template, and start generation from a newline token instead of
  ``<|im_start|>`` so the assistant turn doesn't close itself with an
  immediate EOS.  Add a ``system_prompt`` override to ``render_lance_prompt``
  so per-example QA instructions surface ("Look at the image carefully and
  answer the question.") instead of the caption-style default.
- ``_forward_image_edit``: also stash the PIL image into ``custom_output``
  so callers reading ``outputs[0].output`` *or* ``custom_output["image"]``
  both work — the orchestrator IPC strips the bare ``output`` field across
  process boundaries.
- ``pipeline_registry.py``, ``model_executor/models/lance/*``, registry
  cleanup: remove the dead Lance-specific autoregressive shim
  (``lance.py``) and its stage-input processor; the diffusion pipeline owns
  the full flow now.
- Aspect-ratio buckets aligned with upstream's ``BucketResize`` (6 ratios:
  21:9 / 16:9 / 4:3 / 1:1 / 3:4 / 9:16, sized for ``resolution_vae`` ∈
  {480, 640, 768}).  3:2 / 2:3 dropped — upstream snaps those to 4:3 / 3:4.
- Gradio defaults match upstream's official scripts (cfg_text_scale=4.0,
  validation_num_timesteps=30, validation_timestep_shift=3.5, seed=42); only
  the demo *duration* is intentionally shorter than upstream's 121-frame
  default to keep the UI snappy (user can extend to 10s via slider for
  byte-identical reproduction).

Signed-off-by: lishunyang12 <lishunyang12@163.com>

Signed-off-by: WeiQing Chen <david6666666@users.noreply.github.com>
2026-05-31 11:40:40 +00:00

122 lines
5.0 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Lance chat / system prompts.
Matches the upstream Lance training distribution (see
``bytedance/Lance/data/system_prompt_render.py`` and
``data/common.py::generate_system_prompt``):
* each task has a specific system prompt describing what to attend to;
* the prompt is wrapped in the Qwen chat template
``<|im_start|>system\\n…<|im_end|>\\n<|im_start|>user\\n…<|im_end|>\\n``
``<|im_start|>assistant\\n``;
* vision tokens are framed by ``<|vision_start|><|video_pad|><|vision_end|>``
(upstream uses ``<|video_pad|>`` even for image type by default).
These are pure string formatting helpers — the runtime pipeline still does
the actual VAE / ViT prefill separately.
"""
from __future__ import annotations
# Task-specific system prompts (verbatim from upstream's
# ``generate_system_prompt`` — first variant of each pool).
SYSTEM_PROMPTS: dict[tuple[str, str], str] = {
# (task, vision_type) -> system prompt
("t2i", "image"): (
"Describe the image by detailing the color, quantity, text, shape, "
"size, texture, spatial relationships of the objects and background:"
),
("t2v", "video"): (
"Describe the video by detailing the color, quantity, visible text, "
"shape, size, texture, spatial relationships and motion/camera "
"movements of the objects and background:"
),
# i2v reuses the t2v system prompt (matches upstream Lance PR #33's
# ``data/common.py`` where ``system_prompt_type in ("t2v", "i2v")``
# selects the same prompt pool).
("i2v", "video"): (
"Describe the video by detailing the color, quantity, visible text, "
"shape, size, texture, spatial relationships and motion/camera "
"movements of the objects and background:"
),
("image_edit", "image"): (
"Describe the key features of the input image (color, shape, size, "
"texture, objects, background), then explain how the user's text "
"instruction should alter or modify the image. Generate a new image "
"that meets the user's requirements while maintaining consistency "
"with the original input where appropriate."
),
("video_edit", "video"): (
"Describe the key features of the input video (color, shape, size, "
"texture, objects, background), then explain how the user's text "
"instruction should alter or modify the video. Generate a new video "
"that meets the user's requirements while maintaining consistency "
"with the original input where appropriate."
),
("x2t_image", "image"): (
"Generate a detailed and accurate description of the image, including all the key moments and visual details."
),
("x2t_video", "video"): (
"Generate a detailed and accurate description of the video, including all the key moments and visual details."
),
}
# Vision-content placeholders (Qwen2.5-VL convention). Upstream uses
# ``<|video_pad|>`` for image inputs too unless ``force_video_pad=False``
# (counter-intuitively named; see ``render_qwenvl_prompt`` upstream).
VISION_START = "<|vision_start|>"
VISION_END = "<|vision_end|>"
VIDEO_PAD = "<|video_pad|>"
IMAGE_PAD = "<|image_pad|>"
def render_lance_prompt(
task: str,
user_text: str,
*,
vision_token: str | None = None,
system_prompt: str | None = None,
) -> str:
"""Render a Lance-compatible user-side prompt.
``task`` is one of ``t2i``, ``t2v``, ``image_edit``, ``video_edit``,
``x2t_image``, ``x2t_video``. ``vision_token`` should be the visual
placeholder to embed inside the user message (e.g. ``VIDEO_PAD`` or a
``VISION_START + VIDEO_PAD + VISION_END`` block); pass ``None`` for
text-only inputs. ``system_prompt`` overrides the default task system
prompt — required for x2t QA examples whose upstream JSON carries a
per-example instruction (e.g. ``"Look at the image carefully and answer
the question."``); without it x2t falls back to the caption-style
default and the model describes instead of answering.
Returns a single string ready to be tokenized; no further wrapping is
needed by the caller.
"""
# ``t2v`` / ``x2t_video`` / ``video_edit`` use video system prompts;
# the rest are image.
vision_type = "video" if task in {"t2v", "i2v", "x2t_video", "video_edit"} else "image"
sys_prompt = (
system_prompt if system_prompt else SYSTEM_PROMPTS.get((task, vision_type), "You are a helpful assistant.")
)
if vision_token is None:
user_msg = user_text
else:
# Upstream concatenates the vision block immediately before the text.
user_msg = f"{vision_token}{user_text}"
return (
f"<|im_start|>system\n{sys_prompt}<|im_end|>\n<|im_start|>user\n{user_msg}<|im_end|>\n<|im_start|>assistant\n"
)
__all__ = [
"IMAGE_PAD",
"SYSTEM_PROMPTS",
"VIDEO_PAD",
"VISION_END",
"VISION_START",
"render_lance_prompt",
]