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
317 lines
10 KiB
Python
317 lines
10 KiB
Python
# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
|
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
import os
|
|
import tempfile
|
|
from typing import Any, Awaitable, Callable
|
|
|
|
from tqdm.auto import tqdm
|
|
|
|
from sglang.multimodal_gen.runtime.pipelines_core.schedule_batch import (
|
|
OutputBatch,
|
|
Req,
|
|
)
|
|
from sglang.multimodal_gen.runtime.server_args import ServerArgs
|
|
from sglang.multimodal_gen.runtime.utils.image_io import save_base64_image_to_path
|
|
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
|
|
from sglang.multimodal_gen.runtime.warmup_request_builder import (
|
|
build_warmup_reqs,
|
|
should_include_warmup_image,
|
|
supports_synthetic_warmup,
|
|
)
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
# a 64x64 image because some pipelines reject smaller inputs (e.g. FLUX.2's
|
|
# diffusers image processor requires both dimensions >= 64px)
|
|
MINIMUM_PICTURE_BASE64_FOR_WARMUP = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAS0lEQVR42u3PMQ0AAAwDoEqv9ErYvQQckD4XAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAYHLAB8+AWnmfUycAAAAAElFTkSuQmCC"
|
|
|
|
|
|
def _is_ci_log_env() -> bool:
|
|
return (
|
|
os.environ.get("GITHUB_ACTIONS", "").lower() == "true"
|
|
or os.environ.get("CI", "").lower() == "true"
|
|
)
|
|
|
|
|
|
def get_first_generation_req(req_or_group: Any) -> Req | None:
|
|
"""Extract the first req"""
|
|
if isinstance(req_or_group, Req):
|
|
return req_or_group
|
|
if isinstance(req_or_group, list) and req_or_group:
|
|
first_req = req_or_group[0]
|
|
if isinstance(first_req, Req):
|
|
return first_req
|
|
return None
|
|
|
|
|
|
def is_warmup_req(req_or_group: Any) -> bool:
|
|
"""either server-based or req-based"""
|
|
req = get_first_generation_req(req_or_group)
|
|
return req.is_warmup if req is not None else False
|
|
|
|
|
|
def is_server_based_warmup(req_or_group: Any) -> bool:
|
|
req = get_first_generation_req(req_or_group)
|
|
return (
|
|
req is not None and req.is_warmup and bool(req.extra.get("server_based_warmup"))
|
|
)
|
|
|
|
|
|
def should_return_warmup_result(req_or_group: Any) -> bool:
|
|
# server-based warmup needs to return to the http server to finish the startup
|
|
req = get_first_generation_req(req_or_group)
|
|
return (
|
|
req is not None
|
|
and req.is_warmup
|
|
and bool(req.extra.get("return_warmup_result"))
|
|
)
|
|
|
|
|
|
def should_run_server_warmup(server_args: ServerArgs) -> bool:
|
|
return server_args.warmup and server_args.server_warmup
|
|
|
|
|
|
def is_realtime_serving(server_args: ServerArgs) -> bool:
|
|
"""Synthetic warmup has no realtime session state."""
|
|
try:
|
|
from sglang.multimodal_gen.runtime.entrypoints.openai.realtime.registry import (
|
|
get_realtime_model_adapter,
|
|
)
|
|
|
|
get_realtime_model_adapter(server_args)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def should_run_synthetic_server_warmup(server_args: ServerArgs) -> bool:
|
|
return (
|
|
should_run_server_warmup(server_args)
|
|
and supports_synthetic_warmup(server_args)
|
|
and not is_realtime_serving(server_args)
|
|
)
|
|
|
|
|
|
def should_run_explicit_client_warmup(server_args: ServerArgs) -> bool:
|
|
return (
|
|
server_args.warmup
|
|
and server_args.warmup_resolutions is not None
|
|
and supports_synthetic_warmup(server_args)
|
|
)
|
|
|
|
|
|
def format_warmup_req(req_or_group: Any) -> str:
|
|
req = get_first_generation_req(req_or_group)
|
|
prefix = (
|
|
"server warmup req" if is_server_based_warmup(req_or_group) else "warmup req"
|
|
)
|
|
if req is None:
|
|
return prefix
|
|
|
|
width = getattr(req, "width", None)
|
|
height = getattr(req, "height", None)
|
|
shape = "action" if width is None or height is None else f"{width}x{height}"
|
|
num_frames = getattr(req, "num_frames", None)
|
|
if num_frames is not None and num_frames > 1:
|
|
shape += f"x{num_frames}f"
|
|
|
|
default_steps = req.extra.get("cache_dit_num_inference_steps")
|
|
if default_steps is not None and default_steps != req.num_inference_steps:
|
|
steps = f"{req.num_inference_steps}/{default_steps} steps"
|
|
else:
|
|
steps = f"{req.num_inference_steps} step"
|
|
if req.num_inference_steps != 1:
|
|
steps += "s"
|
|
|
|
return f"{prefix} ({shape}, {steps})"
|
|
|
|
|
|
def build_client_warmup_reqs(
|
|
server_args: ServerArgs,
|
|
*,
|
|
warmup_input_path: str | None = None,
|
|
) -> list[Req]:
|
|
warmup_reqs = build_warmup_reqs(
|
|
server_args,
|
|
warmup_resolutions=server_args.warmup_resolutions,
|
|
warmup_input_path=warmup_input_path,
|
|
return_warmup_result=True,
|
|
server_based_warmup=True,
|
|
)
|
|
warmup_total = sum(1 for req in warmup_reqs if req.is_warmup)
|
|
for req in warmup_reqs:
|
|
if req.is_warmup:
|
|
req.extra["warmup_total"] = warmup_total
|
|
return warmup_reqs
|
|
|
|
|
|
async def run_async_client_warmup(
|
|
server_args: ServerArgs,
|
|
forward: Callable[[Req], Awaitable[OutputBatch]],
|
|
*,
|
|
fail_open: bool = False,
|
|
) -> None:
|
|
try:
|
|
warmup_input_path = None
|
|
if should_include_warmup_image(server_args, server_based_warmup=True):
|
|
warmup_input_path = prepare_warmup_image_path(server_args)
|
|
|
|
for req in build_client_warmup_reqs(
|
|
server_args, warmup_input_path=warmup_input_path
|
|
):
|
|
response = await forward(req)
|
|
if response.error is not None:
|
|
raise RuntimeError(response.error)
|
|
except Exception as e:
|
|
if fail_open:
|
|
logger.warning(
|
|
"Synthetic server warmup failed; continuing startup", exc_info=True
|
|
)
|
|
return
|
|
raise
|
|
|
|
|
|
def run_sync_client_warmup(
|
|
server_args: ServerArgs,
|
|
forward: Callable[[Req], OutputBatch],
|
|
) -> None:
|
|
warmup_input_path = None
|
|
if should_include_warmup_image(server_args, server_based_warmup=True):
|
|
warmup_input_path = prepare_warmup_image_path(server_args)
|
|
|
|
for req in build_client_warmup_reqs(
|
|
server_args, warmup_input_path=warmup_input_path
|
|
):
|
|
response = forward(req)
|
|
if response.error is not None:
|
|
raise RuntimeError(response.error)
|
|
|
|
|
|
def prepare_warmup_image_path(server_args: ServerArgs) -> str:
|
|
if server_args.input_save_path is not None:
|
|
uploads_dir = server_args.input_save_path
|
|
os.makedirs(uploads_dir, exist_ok=True)
|
|
else:
|
|
uploads_dir = tempfile.mkdtemp(prefix="sglang_input_")
|
|
|
|
warmup_image_base = os.path.join(uploads_dir, "warmup_image")
|
|
return save_base64_image_to_path(
|
|
MINIMUM_PICTURE_BASE64_FOR_WARMUP, warmup_image_base
|
|
)
|
|
|
|
|
|
class SchedulerWarmupMixin:
|
|
@staticmethod
|
|
def _format_warmup_req(req_or_group: Any) -> str:
|
|
return format_warmup_req(req_or_group)
|
|
|
|
def _warmup_progress_total(self, req_or_group: Any | None = None) -> int:
|
|
req = get_first_generation_req(req_or_group)
|
|
if req is not None:
|
|
warmup_total = req.extra.get("warmup_total")
|
|
if warmup_total is not None:
|
|
return warmup_total
|
|
|
|
return max(self._warmup_total, 1)
|
|
|
|
def _ensure_warmup_progress_bar(self, req_or_group: Any) -> None:
|
|
if not self._show_warmup_progress:
|
|
return
|
|
|
|
ci_log_env = _is_ci_log_env()
|
|
if self._warmup_progress_bar is None:
|
|
self._warmup_progress_bar = tqdm(
|
|
total=self._warmup_progress_total(req_or_group),
|
|
desc="Warmup requests",
|
|
unit="req",
|
|
disable=ci_log_env,
|
|
)
|
|
if ci_log_env:
|
|
logger.info(
|
|
"Warmup requests: 0/%s %s",
|
|
self._warmup_progress_bar.total,
|
|
self._format_warmup_req(req_or_group),
|
|
)
|
|
self._warmup_progress_bar.set_postfix_str(
|
|
self._format_warmup_req(req_or_group), refresh=False
|
|
)
|
|
|
|
def _advance_warmup_progress_bar(
|
|
self, req_or_group: Any, output_batch: OutputBatch
|
|
) -> None:
|
|
if not self._show_warmup_progress:
|
|
return
|
|
|
|
if self._warmup_progress_bar is None:
|
|
self._ensure_warmup_progress_bar(req_or_group)
|
|
|
|
if output_batch.metrics is not None:
|
|
last_duration_s = output_batch.metrics.total_duration_s
|
|
self._warmup_progress_bar.set_postfix_str(
|
|
f"{self._format_warmup_req(req_or_group)}, last={last_duration_s:.2f}s",
|
|
refresh=False,
|
|
)
|
|
self._warmup_progress_bar.update(1)
|
|
if _is_ci_log_env():
|
|
logger.info(
|
|
"Warmup requests: %s/%s %s",
|
|
self._warmup_progress_bar.n,
|
|
self._warmup_progress_bar.total,
|
|
self._format_warmup_req(req_or_group),
|
|
)
|
|
|
|
if self._warmup_progress_bar.n >= self._warmup_progress_bar.total:
|
|
self._warmup_progress_bar.close()
|
|
self._warmup_progress_bar = None
|
|
|
|
def _log_warmup_result(
|
|
self,
|
|
output_batch: OutputBatch,
|
|
req_or_group: Any,
|
|
is_warmup: bool,
|
|
) -> None:
|
|
if not is_warmup:
|
|
return
|
|
|
|
server_based_warmup = is_server_based_warmup(req_or_group)
|
|
self._warmup_processed += 1
|
|
self._advance_warmup_progress_bar(req_or_group, output_batch)
|
|
|
|
if output_batch.error is None:
|
|
if (
|
|
not server_based_warmup
|
|
and not self._logged_server_ready_after_warmup
|
|
and (
|
|
self._warmup_total <= 0
|
|
or self._warmup_processed >= self._warmup_total
|
|
)
|
|
):
|
|
logger.info("The server is fired up and ready to roll!")
|
|
self._logged_server_ready_after_warmup = True
|
|
else:
|
|
warmup_desc = self._format_warmup_req(req_or_group)
|
|
logger.warning("%s processing failed: %s", warmup_desc, output_batch.error)
|
|
|
|
def process_received_reqs_with_req_based_warmup(
|
|
self, recv_reqs: list[tuple[bytes, Any]]
|
|
) -> list[tuple[bytes, Any]]:
|
|
if (
|
|
self.req_based_warmup_scheduled
|
|
or not self.server_args.warmup
|
|
or not recv_reqs
|
|
or self.server_args.warmup_resolutions is not None
|
|
or self.server_args.server_warmup
|
|
):
|
|
return recv_reqs
|
|
|
|
identity, req_or_group = recv_reqs[0]
|
|
req = get_first_generation_req(req_or_group)
|
|
if req is not None:
|
|
warmup_req = req.copy_as_warmup(self.server_args.warmup_steps)
|
|
recv_reqs.insert(0, (identity, warmup_req))
|
|
self._warmup_total = 1
|
|
self._warmup_processed = 0
|
|
self.req_based_warmup_scheduled = True
|
|
return recv_reqs
|