[VLM] Support request level max_dynamic_patch for OpenAI request (#16268)

Co-authored-by: luoyuan.luo <luoyuan.luo@antgroup.com>
This commit is contained in:
Yuan Luo
2026-01-04 13:04:43 +08:00
committed by GitHub
parent 229938805f
commit 5f3eb377e0
6 changed files with 98 additions and 7 deletions

View File

@@ -339,10 +339,14 @@ class ChatCompletionMessageContentTextPart(BaseModel):
class ChatCompletionMessageContentImageURL(BaseModel):
url: str
detail: Optional[Literal["auto", "low", "high"]] = "auto"
max_dynamic_patch: Optional[int] = None
min_dynamic_patch: Optional[int] = None
class ChatCompletionMessageContentVideoURL(BaseModel):
url: str
max_dynamic_patch: Optional[int] = None
min_dynamic_patch: Optional[int] = None
class ChatCompletionMessageContentAudioURL(BaseModel):
@@ -516,6 +520,10 @@ class ChatCompletionRequest(BaseModel):
stream_reasoning: bool = True
chat_template_kwargs: Optional[Dict] = None
# SGLang multimodal tiling controls (extensions)
max_dynamic_patch: Optional[int] = None
min_dynamic_patch: Optional[int] = None
# Custom logit processor for advanced sampling control
custom_logit_processor: Optional[Union[List[Optional[str]], str]] = None
custom_params: Optional[Dict] = None

View File

@@ -55,6 +55,32 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__)
def _extract_max_dynamic_patch(request: ChatCompletionRequest):
img_vals = []
vid_vals = []
for msg in request.messages or []:
content = getattr(msg, "content", None)
if not isinstance(content, list):
continue
for part in content:
# pydantic object or dict type
if getattr(part, "type", None) == "image_url":
iu = getattr(part, "image_url", None)
mdp = getattr(iu, "max_dynamic_patch", None) if iu else None
if mdp is not None:
img_vals.append(int(mdp))
elif getattr(part, "type", None) == "video_url":
vu = getattr(part, "video_url", None)
mdp = getattr(vu, "max_dynamic_patch", None) if vu else None
if mdp is not None:
vid_vals.append(int(mdp))
# TODO(yuan-luo): per-item max_dynamic_patch for both image and video
img_max_dynamic_patch = min(img_vals) if img_vals else None
vid_max_dynamic_patch = min(vid_vals) if vid_vals else None
return img_max_dynamic_patch, vid_max_dynamic_patch
class OpenAIServingChat(OpenAIServingBase):
"""Handler for /v1/chat/completions requests"""
@@ -195,6 +221,9 @@ class OpenAIServingChat(OpenAIServingBase):
if first_adapter:
self._validate_lora_enabled(first_adapter)
img_max_dynamic_patch, vid_max_dynamic_patch = _extract_max_dynamic_patch(
request
)
adapted_request = GenerateReqInput(
**prompt_kwargs,
image_data=processed_messages.image_data,
@@ -219,6 +248,9 @@ class OpenAIServingChat(OpenAIServingBase):
priority=request.priority,
custom_labels=custom_labels,
custom_logit_processor=request.custom_logit_processor,
image_max_dynamic_patch=img_max_dynamic_patch,
video_max_dynamic_patch=vid_max_dynamic_patch,
max_dynamic_patch=getattr(request, "max_dynamic_patch", None),
)
return adapted_request, request