[Bugfix][Multimodal] Scope cache hash kwargs by modality (#54918)

Signed-off-by: waizuichougou <2082431897@qq.com>
This commit is contained in:
waizuichougou
2026-09-02 14:20:26 +00:00
committed by GitHub
parent 872084fb77
commit c6bca6e585
2 changed files with 88 additions and 10 deletions
+47
View File
@@ -1284,3 +1284,50 @@ def test_processor_inputs_hashes_partial_uuids():
),
]
}
def test_processor_inputs_hashes_scope_kwargs_by_modality():
"""Changing one modality's options must not invalidate another item."""
rng = np.random.RandomState(0)
mm_data_items = MultiModalDataParser().parse_mm_data(
{
"image": [random_image(rng, min_wh=8, max_wh=9)],
"video": [np.zeros((2, 8, 8, 3), dtype=np.uint8)],
}
)
mm_uuid_items = {"image": ["image-uuid"], "video": ["video-uuid"]}
def get_hashes(video_frames: int, image_size: int, video_size: int):
return ProcessorInputs(
prompt=[],
mm_data_items=mm_data_items,
mm_uuid_items=mm_uuid_items,
media_io_kwargs={"video": {"num_frames": video_frames}},
hf_processor_mm_kwargs={
"images_kwargs": {"size": {"longest_edge": image_size}},
"videos_kwargs": {"size": {"longest_edge": video_size}},
},
).get_mm_hashes("test-model", "blake3")
base = get_hashes(video_frames=4, image_size=224, video_size=224)
changed_video = get_hashes(video_frames=16, image_size=224, video_size=448)
changed_image = get_hashes(video_frames=4, image_size=448, video_size=224)
assert changed_video["image"] == base["image"]
assert changed_video["video"] != base["video"]
assert changed_image["image"] != base["image"]
assert changed_image["video"] == base["video"]
def test_processor_inputs_hashes_ignore_unrelated_kwargs():
"""An image-only request ignores video-only processing configuration."""
image = random_image(np.random.RandomState(0), min_wh=8, max_wh=9)
inputs = ProcessorInputs(
prompt=[],
mm_data_items=MultiModalDataParser().parse_mm_data({"image": [image]}),
mm_uuid_items={"image": ["image-uuid"]},
media_io_kwargs={"video": {"num_frames": 16}},
hf_processor_mm_kwargs={"videos_kwargs": {"size": {"longest_edge": 448}}},
)
assert inputs.get_mm_hashes("test-model", "blake3") == {"image": ["image-uuid"]}
+41 -10
View File
@@ -9,6 +9,12 @@ from vllm.inputs import MultiModalHashes
from ..hasher import MultiModalHasher
from ..parse import MultiModalDataItems, MultiModalUUIDItems
_HF_MODALITY_PROCESSOR_KWARGS = {
"image": "images_kwargs",
"video": "videos_kwargs",
"audio": "audio_kwargs",
}
@dataclass
class ProcessorInputs:
@@ -31,17 +37,41 @@ class ProcessorInputs:
mm_data_items = self.mm_data_items
mm_uuid_items = self.mm_uuid_items or {}
hash_factors = {
"media_io_kwargs": dict(self.media_io_kwargs),
"mm_processor_kwargs": dict(self.hf_processor_mm_kwargs),
}
has_hash_factors = any(hash_factors.values())
hash_factor_kwargs = hash_factors if has_hash_factors else {}
mm_hashes = dict[str, list[str]]()
hasher = MultiModalHasher
for modality, data_items in mm_data_items.items():
# Each cache entry represents one item from one modality. Keep
# modality-scoped options out of unrelated entries, while
# retaining flat processor kwargs because they are shared by HF.
if modality in _HF_MODALITY_PROCESSOR_KWARGS:
modality_media_io_kwargs = self.media_io_kwargs.get(modality, {})
media_io_kwargs = (
{modality: modality_media_io_kwargs}
if modality_media_io_kwargs
else {}
)
scoped_key = _HF_MODALITY_PROCESSOR_KWARGS[modality]
mm_processor_kwargs = {
key: value
for key, value in self.hf_processor_mm_kwargs.items()
if key not in _HF_MODALITY_PROCESSOR_KWARGS.values()
or key == scoped_key
}
else:
# Unified modalities such as ``vision_chunk`` can contain
# items originating from multiple media modalities. There is
# no per-item origin metadata here, so preserve all factors.
media_io_kwargs = dict(self.media_io_kwargs)
mm_processor_kwargs = dict(self.hf_processor_mm_kwargs)
hash_factors = {
"media_io_kwargs": media_io_kwargs,
"mm_processor_kwargs": mm_processor_kwargs,
}
hash_factors = {key: value for key, value in hash_factors.items() if value}
has_hash_factors = bool(hash_factors)
uuid_items = (
mm_uuid_items[modality]
if modality in mm_uuid_items
@@ -53,8 +83,9 @@ class ProcessorInputs:
for i, item in enumerate(data_items.get_all_items_for_hash()):
uuid_item = uuid_items[i]
# NOTE: Even if a uuid_item is provided, model output
# depends on hash_factor_kwargs, so they are taken into account.
# NOTE: Even if a uuid_item is provided, model output depends
# on the current modality's hash factors, so they are taken
# into account.
if uuid_item is None or has_hash_factors:
# NOTE: use provided hash string to hash with kwargs
# if available for better performance.
@@ -64,7 +95,7 @@ class ProcessorInputs:
hash_algorithm,
model_id=model_id,
**{modality: item},
**hash_factor_kwargs,
**hash_factors,
)
)
else: