[V1][VLM] V1 support for selected single-image models. (#11632)
Signed-off-by: Roger Wang <ywang@roblox.com> Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk> Signed-off-by: Isotr0py <2037008807@qq.com> Co-authored-by: DarkLight1337 <tlleungac@connect.ust.hk> Co-authored-by: Isotr0py <2037008807@qq.com>
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
import pickle
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable, ItemsView, Iterable, Mapping, Sequence
|
||||
from dataclasses import dataclass, field
|
||||
from functools import lru_cache
|
||||
from typing import Any, NamedTuple, Optional, Protocol, TypeVar, Union
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import torch
|
||||
from blake3 import blake3
|
||||
from PIL.Image import Image
|
||||
from PIL import Image
|
||||
from transformers import BatchFeature, ProcessorMixin
|
||||
|
||||
from vllm.inputs import DummyData, InputProcessingContext
|
||||
@@ -353,13 +355,13 @@ def _replace_matches(
|
||||
) -> list[_S]:
|
||||
out_seqs = list[_S]()
|
||||
prev_end_idx = 0
|
||||
next_idx_by_modality = {modality: 0 for modality in mm_item_counts}
|
||||
next_idx_by_modality = defaultdict[str, int](lambda: 0)
|
||||
|
||||
for match in _resolve_matches(prompt, matches):
|
||||
modality = match.modality
|
||||
|
||||
item_idx = next_idx_by_modality[modality]
|
||||
if item_idx >= mm_item_counts[modality]:
|
||||
if item_idx >= mm_item_counts.get(modality, 0):
|
||||
continue
|
||||
|
||||
start_idx = match.start_idx
|
||||
@@ -513,7 +515,7 @@ class ProcessingCache:
|
||||
return obj.encode("utf-8")
|
||||
if isinstance(obj, bytes):
|
||||
return obj
|
||||
if isinstance(obj, Image):
|
||||
if isinstance(obj, Image.Image):
|
||||
return obj.tobytes()
|
||||
|
||||
# Convertible to NumPy arrays
|
||||
@@ -673,10 +675,14 @@ class BaseMultiModalProcessor(ABC):
|
||||
Given the original multi-modal items for this modality
|
||||
and HF-processed data, output the replacements to perform.
|
||||
|
||||
Note:
|
||||
Even when the HF processor already performs replacement for us,
|
||||
we still use this replacement information to determine
|
||||
the placeholder token positions for each multi-modal item.
|
||||
Notes:
|
||||
- You should not assume that HF processor always performs prompt
|
||||
replacement: in :meth:`_apply_hf_processor_missing`, this method
|
||||
is called on text-only and multimodal-only inputs separately,
|
||||
instead of passing them in the same call.
|
||||
- The replacement information returned by this method is also used
|
||||
to determine the placeholder token positions for each multi-modal
|
||||
item.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -710,6 +716,10 @@ class BaseMultiModalProcessor(ABC):
|
||||
mm_data: Mapping[str, object],
|
||||
mm_kwargs: Mapping[str, object],
|
||||
) -> BatchFeature:
|
||||
"""
|
||||
Call the HF processor on the prompt text and
|
||||
associated multi-modal data.
|
||||
"""
|
||||
return self.ctx.call_hf_processor(
|
||||
self._get_hf_processor(**mm_kwargs),
|
||||
dict(text=prompt, **mm_data),
|
||||
@@ -723,7 +733,8 @@ class BaseMultiModalProcessor(ABC):
|
||||
hf_processor_mm_kwargs: Mapping[str, object],
|
||||
) -> tuple[list[int], MultiModalKwargs]:
|
||||
"""
|
||||
Apply the HF processor on the full prompt text and multi-modal data.
|
||||
Wrapper of :meth:`_call_hf_processor` that applies
|
||||
additional pre-processing and post-processing.
|
||||
"""
|
||||
processor_data, passthrough_data = self._get_hf_mm_data(mm_items)
|
||||
|
||||
@@ -754,10 +765,11 @@ class BaseMultiModalProcessor(ABC):
|
||||
Apply the HF processor on the full prompt text, but only on the
|
||||
multi-modal data that are missing from the cache.
|
||||
|
||||
Note: We pass prompt text and multi-modal data into the HF processor
|
||||
in separate calls to avoid HF prompt replacement being done for
|
||||
cached items; instead, we rely on our own prompt replacement logic
|
||||
for the full text.
|
||||
Note:
|
||||
We pass prompt text and multi-modal data into the HF processor
|
||||
in separate calls to avoid HF prompt replacement being done for
|
||||
cached items; instead, we rely on our own prompt replacement logic
|
||||
(:meth:`_get_prompt_replacements`) for the full text.
|
||||
"""
|
||||
mm_missing_counts = mm_missing_data_items.get_all_counts()
|
||||
|
||||
@@ -1010,6 +1022,36 @@ class BaseMultiModalProcessor(ABC):
|
||||
mm_placeholders=mm_placeholders,
|
||||
)
|
||||
|
||||
def _get_dummy_audios(
|
||||
self,
|
||||
*,
|
||||
length: int,
|
||||
num_audios: int,
|
||||
) -> list[npt.NDArray]:
|
||||
audio = np.zeros((length, ))
|
||||
return [audio] * num_audios
|
||||
|
||||
def _get_dummy_images(
|
||||
self,
|
||||
*,
|
||||
width: int,
|
||||
height: int,
|
||||
num_images: int,
|
||||
) -> list[Image.Image]:
|
||||
image = Image.new("RGB", (width, height), color=0)
|
||||
return [image] * num_images
|
||||
|
||||
def _get_dummy_videos(
|
||||
self,
|
||||
*,
|
||||
width: int,
|
||||
height: int,
|
||||
num_frames: int,
|
||||
num_videos: int,
|
||||
) -> list[npt.NDArray]:
|
||||
video = np.zeros((num_frames, width, height, 3))
|
||||
return [video] * num_videos
|
||||
|
||||
@abstractmethod
|
||||
def _get_dummy_mm_inputs(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user