94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
269 lines
10 KiB
Python
269 lines
10 KiB
Python
import asyncio
|
|
import os
|
|
from typing import Dict, List, Optional, Union
|
|
|
|
import numpy as np
|
|
from transformers.models.auto.processing_auto import (
|
|
PROCESSOR_MAPPING_NAMES as HF_MAPPING_NAMES,
|
|
)
|
|
|
|
import sglang.srt.managers.multimodal_processor as sgl_mm_processor_utils
|
|
from sglang.srt.managers.schedule_batch import (
|
|
Modality,
|
|
MultimodalDataItem,
|
|
MultimodalProcessorOutput,
|
|
)
|
|
from sglang.srt.models.llava import (
|
|
LlavaForConditionalGeneration,
|
|
LlavaLlamaForCausalLM,
|
|
LlavaMistralForCausalLM,
|
|
LlavaQwenForCausalLM,
|
|
)
|
|
from sglang.srt.models.llavavid import LlavaVidForCausalLM
|
|
from sglang.srt.models.mistral import Mistral3ForConditionalGeneration
|
|
from sglang.srt.multimodal.mm_utils import (
|
|
ensure_numpy,
|
|
expand2square,
|
|
process_anyres_image,
|
|
)
|
|
from sglang.srt.multimodal.processors.base_processor import BaseMultimodalProcessor
|
|
from sglang.srt.utils import ImageData, load_image, logger
|
|
from sglang.utils import get_exception_traceback
|
|
|
|
|
|
class LlavaImageProcessor(BaseMultimodalProcessor):
|
|
models = [
|
|
LlavaLlamaForCausalLM,
|
|
LlavaVidForCausalLM,
|
|
LlavaQwenForCausalLM,
|
|
LlavaMistralForCausalLM,
|
|
]
|
|
gpu_image_decode = False # Llava processes loaded image as PIL image explicitly
|
|
|
|
def __init__(self, hf_config, server_args, _processor, *args, **kwargs):
|
|
super().__init__(hf_config, server_args, _processor, *args, **kwargs)
|
|
|
|
@staticmethod
|
|
def _process_single_image_task(
|
|
image_data: Union[str, bytes, ImageData],
|
|
image_aspect_ratio: Optional[str] = None,
|
|
image_grid_pinpoints: Optional[str] = None,
|
|
processor=None,
|
|
):
|
|
|
|
image_processor = processor.image_processor
|
|
|
|
try:
|
|
url = image_data.url if isinstance(image_data, ImageData) else image_data
|
|
image, image_size = load_image(url, False)
|
|
if image_size is not None:
|
|
# It is a video with multiple images
|
|
image_hash = hash(url)
|
|
pixel_values = image_processor(image)["pixel_values"]
|
|
for i in range(len(pixel_values)):
|
|
pixel_values[i] = ensure_numpy(pixel_values[i]).astype(np.float16)
|
|
pixel_values = np.stack(pixel_values, axis=0)
|
|
return pixel_values, image_hash, image_size
|
|
else:
|
|
# It is an image
|
|
image_hash = hash(url)
|
|
if image_aspect_ratio == "pad":
|
|
image = expand2square(
|
|
image,
|
|
tuple(int(x * 255) for x in image_processor.image_mean),
|
|
)
|
|
pixel_values = image_processor(image.convert("RGB"))[
|
|
"pixel_values"
|
|
][0]
|
|
elif image_aspect_ratio == "anyres" or (
|
|
image_aspect_ratio is not None
|
|
and "anyres_max" in image_aspect_ratio
|
|
):
|
|
pixel_values = process_anyres_image(
|
|
image, image_processor, image_grid_pinpoints
|
|
)
|
|
else:
|
|
pixel_values = image_processor(image)["pixel_values"][0]
|
|
|
|
pixel_values = ensure_numpy(pixel_values)
|
|
if isinstance(pixel_values, np.ndarray):
|
|
pixel_values = pixel_values.astype(np.float16)
|
|
|
|
return pixel_values, image_hash, image.size
|
|
except Exception:
|
|
logger.error("Exception in TokenizerManager:\n" + get_exception_traceback())
|
|
|
|
async def _process_single_image(
|
|
self,
|
|
image_data: Union[bytes, str, ImageData],
|
|
aspect_ratio: str,
|
|
grid_pinpoints: str,
|
|
):
|
|
if self.cpu_executor is not None:
|
|
loop = asyncio.get_running_loop()
|
|
fut = loop.run_in_executor(
|
|
self.cpu_executor,
|
|
LlavaImageProcessor._process_single_image_task,
|
|
image_data,
|
|
aspect_ratio,
|
|
grid_pinpoints,
|
|
self._processor,
|
|
)
|
|
timeout = int(os.environ.get("REQUEST_TIMEOUT", "10"))
|
|
return await asyncio.wait_for(fut, timeout=timeout)
|
|
else:
|
|
return self._process_single_image_task(
|
|
image_data,
|
|
aspect_ratio,
|
|
grid_pinpoints,
|
|
self._processor.image_processor,
|
|
)
|
|
|
|
def _process_precomputed_image_data(self, image_data: List[Dict]) -> Dict:
|
|
mm_items = []
|
|
for item in image_data:
|
|
# Infer size logic...
|
|
if "image_sizes" not in item:
|
|
if "pixel_values" in item:
|
|
pv = item["pixel_values"]
|
|
# Handle simplified if/else
|
|
h, w = (
|
|
(pv.shape[2], pv.shape[3])
|
|
if len(pv.shape) == 4
|
|
else (pv.shape[1], pv.shape[2])
|
|
)
|
|
item["image_sizes"] = [(w, h)]
|
|
else:
|
|
item["image_sizes"] = [(336, 336)]
|
|
|
|
mm_items.append(
|
|
MultimodalDataItem(
|
|
feature=item["feature"],
|
|
modality=Modality.IMAGE,
|
|
model_specific_data=item,
|
|
)
|
|
)
|
|
return MultimodalProcessorOutput(mm_items=mm_items)
|
|
|
|
async def process_mm_data_async(
|
|
self,
|
|
image_data: List[Union[str, bytes, ImageData]],
|
|
input_text,
|
|
request_obj,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
# FIX: Handle precomputed embeddings (dictionaries)
|
|
# If the input is already a dictionary, we skip the CPU image processor.
|
|
# We also need to infer 'image_sizes' from 'pixel_values' if missing,
|
|
# because pad_input_ids requires it.
|
|
if (
|
|
isinstance(image_data, list)
|
|
and len(image_data) > 0
|
|
and isinstance(image_data[0], dict)
|
|
):
|
|
return self._process_precomputed_image_data(image_data)
|
|
|
|
modalities = request_obj.modalities or ["image"]
|
|
aspect_ratio = getattr(self.hf_config, "image_aspect_ratio", None)
|
|
grid_pinpoints = (
|
|
self.hf_config.image_grid_pinpoints
|
|
if hasattr(self.hf_config, "image_grid_pinpoints")
|
|
and "anyres" in aspect_ratio
|
|
else None
|
|
)
|
|
|
|
if isinstance(image_data, list) and len(image_data) > 0:
|
|
if "multi-images" in modalities or "video" in modalities:
|
|
# Multiple images
|
|
aspect_ratio = "pad" # LLaVA OneVision Handling: more than one image --> interleaved image mode or video mode. We do not use anyres
|
|
pixel_values, data_hashes, image_sizes = [], [], []
|
|
res = []
|
|
for img_data in image_data:
|
|
res.append(
|
|
self._process_single_image(
|
|
img_data, aspect_ratio, grid_pinpoints
|
|
)
|
|
)
|
|
|
|
res = await asyncio.gather(*res)
|
|
for pixel_v, image_h, image_s in res:
|
|
pixel_values.append(pixel_v)
|
|
data_hashes.append(image_h)
|
|
image_sizes.append(image_s)
|
|
else:
|
|
# A single image
|
|
pixel_values, image_hash, image_size = await self._process_single_image(
|
|
image_data[0], aspect_ratio, grid_pinpoints
|
|
)
|
|
pixel_values = [pixel_values]
|
|
image_sizes = [image_size]
|
|
else:
|
|
raise ValueError(f"Invalid image data: {image_data}")
|
|
modality = Modality.IMAGE
|
|
if isinstance(request_obj.modalities, list):
|
|
if request_obj.modalities[0] == "video":
|
|
modality = Modality.VIDEO
|
|
|
|
# Create one item per image for better cache granularity
|
|
mm_items = []
|
|
for pixel_v, image_s in zip(pixel_values, image_sizes):
|
|
# Ensure ndim=4 so the model forward takes the correct encode branch
|
|
if isinstance(pixel_v, np.ndarray) and pixel_v.ndim == 3:
|
|
pixel_v = np.expand_dims(pixel_v, 0)
|
|
mm_items.append(
|
|
MultimodalDataItem(
|
|
feature=pixel_v,
|
|
model_specific_data={
|
|
"image_sizes": [image_s],
|
|
"image_aspect_ratio": aspect_ratio,
|
|
},
|
|
modality=modality,
|
|
)
|
|
)
|
|
|
|
return MultimodalProcessorOutput(
|
|
mm_items=mm_items,
|
|
)
|
|
|
|
|
|
class LlavaMultimodalProcessor(BaseMultimodalProcessor):
|
|
"""
|
|
This is a wrapper class used to identify the multimodal processor for Llava architectures' vision model.
|
|
"""
|
|
|
|
models = [LlavaForConditionalGeneration, Mistral3ForConditionalGeneration]
|
|
|
|
def _get_sgl_processor_cls(self, model_type: str):
|
|
if model_type == "clip_vision_model":
|
|
return LlavaImageProcessor
|
|
if hf_name := HF_MAPPING_NAMES.get(model_type):
|
|
sgl_mm_processor_set = sgl_mm_processor_utils.PROCESSOR_MAPPING.values()
|
|
sgl_processor_cls = list(
|
|
filter(lambda p: p.__name__ == hf_name, sgl_mm_processor_set)
|
|
)
|
|
if sgl_processor_cls:
|
|
return sgl_processor_cls[0]
|
|
raise ValueError(
|
|
f"Cannot find corresponding multimodal processor registered in sglang for model type `{model_type}`"
|
|
)
|
|
|
|
def __init__(self, hf_config, server_args, _processor, *args, **kwargs):
|
|
assert hasattr(hf_config, "vision_config")
|
|
assert hasattr(hf_config, "text_config")
|
|
self.vision_config = hf_config.vision_config
|
|
self.text_config = hf_config.text_config
|
|
self.hf_config = hf_config
|
|
|
|
if vision_type := getattr(self.vision_config, "model_type"):
|
|
self.inner = self._get_sgl_processor_cls(vision_type)(
|
|
hf_config, server_args, _processor, *args, **kwargs
|
|
)
|
|
else:
|
|
raise ValueError(
|
|
f"Required `vision_config.model_type` is not found in hf_config: `{hf_config}`"
|
|
)
|
|
|
|
async def process_mm_data_async(self, *args, **kwargs):
|
|
return await self.inner.process_mm_data_async(*args, **kwargs)
|