fix: warm up Kimi VLM vision encoder at startup (#31298)
This commit is contained in:
@@ -2023,6 +2023,46 @@ def _admin_api_key_missing_response(
|
|||||||
# Minimal 32x32 black PNG (base64, GLM4v requires at least 32x32 sized image)
|
# Minimal 32x32 black PNG (base64, GLM4v requires at least 32x32 sized image)
|
||||||
MINIMUM_PNG_PICTURE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAbUlEQVRYhe3VsQ2AMAxE0Y/lIgNQULD/OqyCMgCihCKSG4yRuKuiNH6JLsoEbMACOGBcua9HOR7Y6w6swBwMy0qLTpkeI77qdEBpBFAHBBDAGH8WrwJKI4AAegUCfAKgEgpQDvh3CR3oQCuav58qlAw73kKCSgAAAABJRU5ErkJggg=="
|
MINIMUM_PNG_PICTURE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAbUlEQVRYhe3VsQ2AMAxE0Y/lIgNQULD/OqyCMgCihCKSG4yRuKuiNH6JLsoEbMACOGBcua9HOR7Y6w6swBwMy0qLTpkeI77qdEBpBFAHBBDAGH8WrwJKI4AAegUCfAKgEgpQDvh3CR3oQCuav58qlAw73kKCSgAAAABJRU5ErkJggg=="
|
||||||
|
|
||||||
|
# Kimi K2.5/K2.7 runs its MoonViT position interpolation through torch.compile.
|
||||||
|
# The minimal image above does not exercise a representative image shape and
|
||||||
|
# leaves the first client request paying the compilation cost. Keep this
|
||||||
|
# narrowly scoped: a larger default warmup image would unnecessarily lengthen
|
||||||
|
# startup for VLMs whose encoders do not have this behavior.
|
||||||
|
KIMI_VLM_WARMUP_PNG_PICTURE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAADEUlEQVR42u3BgQAAAADDoPlTX+EAVQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBvArQAAf/YBFAAAAAASUVORK5CYII="
|
||||||
|
|
||||||
|
# Kimi K3's native vision preprocessing uses a 14x14 spatial patch size. A
|
||||||
|
# 448x448 image exercises a representative 32x32 patch grid without padding.
|
||||||
|
KIMI_K3_VLM_WARMUP_PNG_PICTURE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAcAAAAHACAIAAAC6Ry8kAAACX0lEQVR42u3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAtwEyRwAB32QPDQAAAABJRU5ErkJggg=="
|
||||||
|
|
||||||
|
|
||||||
|
def _get_vlm_warmup_image_base64(model_info: dict) -> str:
|
||||||
|
"""Choose the VLM image used by the startup warmup request.
|
||||||
|
|
||||||
|
A 512x512 image triggers Kimi K2.5/K2.7's representative compiled
|
||||||
|
position-interpolation path during startup. Kimi K3 uses a 448x448 image
|
||||||
|
matching its native vision patch grid. This keeps one-time vision setup
|
||||||
|
work out of the first external image request.
|
||||||
|
Other VLMs retain the minimal image to avoid changing their startup cost.
|
||||||
|
"""
|
||||||
|
|
||||||
|
architectures = model_info.get("architectures") or []
|
||||||
|
if (
|
||||||
|
"KimiK3ForConditionalGeneration" in architectures
|
||||||
|
or model_info.get("model_type") == "kimi_k3"
|
||||||
|
):
|
||||||
|
logger.info(
|
||||||
|
"Using a 448x448 image for Kimi K3 VLM startup warmup to exercise "
|
||||||
|
"its native 32x32 vision patch grid."
|
||||||
|
)
|
||||||
|
return KIMI_K3_VLM_WARMUP_PNG_PICTURE_BASE64
|
||||||
|
if "KimiK25ForConditionalGeneration" in architectures:
|
||||||
|
logger.info(
|
||||||
|
"Using a 512x512 image for Kimi VLM startup warmup to compile "
|
||||||
|
"MoonViT position interpolation."
|
||||||
|
)
|
||||||
|
return KIMI_VLM_WARMUP_PNG_PICTURE_BASE64
|
||||||
|
return MINIMUM_PNG_PICTURE_BASE64
|
||||||
|
|
||||||
|
|
||||||
async def _send_disaggregation_warmup_requests(
|
async def _send_disaggregation_warmup_requests(
|
||||||
server_args: ServerArgs,
|
server_args: ServerArgs,
|
||||||
@@ -2137,7 +2177,8 @@ def _execute_server_warmup(server_args: ServerArgs):
|
|||||||
{
|
{
|
||||||
"type": "image_url",
|
"type": "image_url",
|
||||||
"image_url": {
|
"image_url": {
|
||||||
"url": f"data:image/png;base64,{MINIMUM_PNG_PICTURE_BASE64}"
|
"url": "data:image/png;base64,"
|
||||||
|
f"{_get_vlm_warmup_image_base64(model_info)}"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
"""Unit tests for model-specific server warmup inputs."""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import struct
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from sglang.srt.entrypoints.http_server import (
|
||||||
|
KIMI_K3_VLM_WARMUP_PNG_PICTURE_BASE64,
|
||||||
|
KIMI_VLM_WARMUP_PNG_PICTURE_BASE64,
|
||||||
|
MINIMUM_PNG_PICTURE_BASE64,
|
||||||
|
_get_vlm_warmup_image_base64,
|
||||||
|
)
|
||||||
|
from sglang.test.ci.ci_register import register_cpu_ci
|
||||||
|
from sglang.test.test_utils import CustomTestCase
|
||||||
|
|
||||||
|
register_cpu_ci(est_time=5, suite="base-a-test-cpu")
|
||||||
|
|
||||||
|
|
||||||
|
class TestVlmWarmupImage(CustomTestCase):
|
||||||
|
def test_kimi_k2_uses_representative_vision_image(self):
|
||||||
|
image_base64 = _get_vlm_warmup_image_base64(
|
||||||
|
{"architectures": ["KimiK25ForConditionalGeneration"]}
|
||||||
|
)
|
||||||
|
self.assertEqual(image_base64, KIMI_VLM_WARMUP_PNG_PICTURE_BASE64)
|
||||||
|
|
||||||
|
png = base64.b64decode(KIMI_VLM_WARMUP_PNG_PICTURE_BASE64)
|
||||||
|
self.assertEqual(png[:8], b"\x89PNG\r\n\x1a\n")
|
||||||
|
self.assertEqual(struct.unpack(">II", png[16:24]), (512, 512))
|
||||||
|
|
||||||
|
def test_kimi_k3_uses_native_patch_grid_image(self):
|
||||||
|
for model_info in (
|
||||||
|
{"architectures": ["KimiK3ForConditionalGeneration"]},
|
||||||
|
{"architectures": None, "model_type": "kimi_k3"},
|
||||||
|
):
|
||||||
|
with self.subTest(model_info=model_info):
|
||||||
|
self.assertEqual(
|
||||||
|
_get_vlm_warmup_image_base64(model_info),
|
||||||
|
KIMI_K3_VLM_WARMUP_PNG_PICTURE_BASE64,
|
||||||
|
)
|
||||||
|
|
||||||
|
png = base64.b64decode(KIMI_K3_VLM_WARMUP_PNG_PICTURE_BASE64)
|
||||||
|
self.assertEqual(png[:8], b"\x89PNG\r\n\x1a\n")
|
||||||
|
self.assertEqual(struct.unpack(">II", png[16:24]), (448, 448))
|
||||||
|
|
||||||
|
def test_other_vlms_keep_minimal_startup_image(self):
|
||||||
|
self.assertEqual(
|
||||||
|
_get_vlm_warmup_image_base64(
|
||||||
|
{"architectures": ["Qwen3VLForConditionalGeneration"]}
|
||||||
|
),
|
||||||
|
MINIMUM_PNG_PICTURE_BASE64,
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
_get_vlm_warmup_image_base64({"architectures": None}),
|
||||||
|
MINIMUM_PNG_PICTURE_BASE64,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user