chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,452 @@
|
||||
# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
|
||||
import asyncio
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Generator, List, Optional, Union
|
||||
|
||||
import httpx
|
||||
from fastapi import HTTPException, UploadFile
|
||||
|
||||
from sglang.multimodal_gen.configs.sample.sampling_params import (
|
||||
DataType,
|
||||
SamplingParams,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.entrypoints.utils import (
|
||||
ListLorasReq,
|
||||
MergeLoraWeightsReq,
|
||||
SetLoraReq,
|
||||
ShutdownReq,
|
||||
UnmergeLoraWeightsReq,
|
||||
format_lora_message,
|
||||
save_outputs,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.pipelines_core.schedule_batch import OutputBatch
|
||||
from sglang.multimodal_gen.runtime.scheduler_client import AsyncSchedulerClient
|
||||
from sglang.multimodal_gen.runtime.server_args import get_global_server_args
|
||||
from sglang.multimodal_gen.runtime.utils.common import parse_size
|
||||
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,
|
||||
log_batch_completion,
|
||||
log_generation_timer,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.utils.trace_wrapper import trace_req
|
||||
|
||||
# re-export LoRA protocol types for backward compatibility
|
||||
__all__ = [
|
||||
"SetLoraReq",
|
||||
"MergeLoraWeightsReq",
|
||||
"UnmergeLoraWeightsReq",
|
||||
"ListLorasReq",
|
||||
"ShutdownReq",
|
||||
"format_lora_message",
|
||||
]
|
||||
|
||||
logger = init_logger(__name__)
|
||||
|
||||
OUTPUT_QUALITY_MAPPER = {"maximum": 100, "high": 90, "medium": 55, "low": 35}
|
||||
DEFAULT_FPS = 24
|
||||
DEFAULT_VIDEO_SECONDS = 4
|
||||
|
||||
|
||||
def _bad_request(message: str) -> HTTPException:
|
||||
return HTTPException(status_code=400, detail=message)
|
||||
|
||||
|
||||
def _parse_size_or_raise(size: str) -> tuple[int, int]:
|
||||
width, height = parse_size(size)
|
||||
if width is None or height is None or width <= 0 or height <= 0:
|
||||
raise _bad_request("size must be formatted as positive WIDTHxHEIGHT")
|
||||
return width, height
|
||||
|
||||
|
||||
def _validate_positive_int(kwargs: dict[str, Any], name: str) -> None:
|
||||
value = kwargs.get(name)
|
||||
if value is not None and int(value) <= 0:
|
||||
raise _bad_request(f"{name} must be positive")
|
||||
|
||||
|
||||
def flatten_extra_params(payload: Any) -> dict[str, Any]:
|
||||
"""Promote vLLM-Omni-style extra_params into regular request fields."""
|
||||
if not isinstance(payload, dict):
|
||||
return {}
|
||||
|
||||
extra_params = payload.pop("extra_params", None)
|
||||
if isinstance(extra_params, str):
|
||||
try:
|
||||
extra_params = json.loads(extra_params)
|
||||
except Exception:
|
||||
extra_params = None
|
||||
if not isinstance(extra_params, dict):
|
||||
if "guardrails" in payload:
|
||||
payload.setdefault("use_guardrails", payload["guardrails"])
|
||||
return payload
|
||||
|
||||
for key, value in extra_params.items():
|
||||
payload.setdefault(key, value)
|
||||
if "guardrails" in extra_params:
|
||||
payload.setdefault("use_guardrails", extra_params["guardrails"])
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
@contextmanager
|
||||
def temp_dir_if_disabled(
|
||||
configured_path: str | None,
|
||||
) -> Generator[str, None, None]:
|
||||
"""Yield *configured_path* when it is set, otherwise create a temporary
|
||||
directory that is automatically removed when the context exits."""
|
||||
if configured_path is not None:
|
||||
os.makedirs(configured_path, exist_ok=True)
|
||||
yield configured_path
|
||||
else:
|
||||
tmp = tempfile.mkdtemp(prefix="sglang_")
|
||||
try:
|
||||
yield tmp
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
def choose_output_image_ext(
|
||||
output_format: Optional[str], background: Optional[str]
|
||||
) -> str:
|
||||
fmt = (output_format or "").lower()
|
||||
if fmt in {"png", "webp", "jpeg", "jpg"}:
|
||||
return "jpg" if fmt == "jpeg" else fmt
|
||||
if (background or "auto").lower() == "transparent":
|
||||
return "png"
|
||||
return "jpg"
|
||||
|
||||
|
||||
def build_sampling_params(request_id: str, **kwargs) -> SamplingParams:
|
||||
"""Build SamplingParams from request parameters.
|
||||
|
||||
Handles size parsing, output_quality resolution, and None filtering before
|
||||
delegating to SamplingParams.from_user_sampling_params_args. Callers pass
|
||||
only the parameters they have; None values are stripped automatically so
|
||||
that SamplingParams defaults apply.
|
||||
"""
|
||||
server_args = get_global_server_args()
|
||||
|
||||
# pop HTTP-layer params that aren't SamplingParams fields
|
||||
output_quality = kwargs.pop("output_quality", None)
|
||||
|
||||
has_explicit_compression = kwargs.get("output_compression") is not None
|
||||
|
||||
# parse "WxH" size string if provided
|
||||
size = kwargs.pop("size", None)
|
||||
if size:
|
||||
w, h = _parse_size_or_raise(size)
|
||||
# treat None dimensions as unset so parsed size can fill them
|
||||
if kwargs.get("width") is None:
|
||||
kwargs["width"] = w
|
||||
if kwargs.get("height") is None:
|
||||
kwargs["height"] = h
|
||||
|
||||
for name in (
|
||||
"width",
|
||||
"height",
|
||||
"num_frames",
|
||||
"num_inference_steps",
|
||||
"num_outputs_per_prompt",
|
||||
):
|
||||
_validate_positive_int(kwargs, name)
|
||||
|
||||
# filter out None values to let SamplingParams defaults apply
|
||||
kwargs = {k: v for k, v in kwargs.items() if v is not None}
|
||||
kwargs.setdefault("save_output", True)
|
||||
|
||||
sampling_params = SamplingParams.from_user_sampling_params_args(
|
||||
model_path=server_args.model_path,
|
||||
server_args=server_args,
|
||||
request_id=request_id,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# resolve output_quality → output_compression with the correct data_type.
|
||||
# SamplingParams.__post_init__ may have resolved with the wrong data_type
|
||||
# (default VIDEO) before _adjust() set the correct one.
|
||||
if not has_explicit_compression and output_quality is not None:
|
||||
resolved = adjust_output_quality(output_quality, sampling_params.data_type)
|
||||
if resolved is not None:
|
||||
sampling_params.output_compression = resolved
|
||||
|
||||
return sampling_params
|
||||
|
||||
|
||||
async def save_image_to_path(
|
||||
image: Union[UploadFile, bytes, str],
|
||||
target_path: str,
|
||||
*,
|
||||
prefer_remote_source: bool = False,
|
||||
) -> str:
|
||||
input_path = await _maybe_url_image(
|
||||
image, target_path, prefer_remote_source=prefer_remote_source
|
||||
)
|
||||
if input_path is None:
|
||||
input_path = await _save_upload_to_path(image, target_path)
|
||||
return input_path
|
||||
|
||||
|
||||
# Helpers
|
||||
async def _save_upload_to_path(
|
||||
upload: Union[UploadFile, bytes], target_path: str
|
||||
) -> str:
|
||||
os.makedirs(os.path.dirname(target_path), exist_ok=True)
|
||||
if isinstance(upload, bytes):
|
||||
content = upload
|
||||
elif isinstance(upload, (bytearray, memoryview)):
|
||||
content = bytes(upload)
|
||||
else:
|
||||
read = getattr(upload, "read", None)
|
||||
if not callable(read):
|
||||
raise TypeError(f"Unsupported image upload type: {type(upload).__name__}")
|
||||
content = read()
|
||||
if inspect.isawaitable(content):
|
||||
content = await content
|
||||
if isinstance(content, (bytearray, memoryview)):
|
||||
content = bytes(content)
|
||||
if not isinstance(content, bytes):
|
||||
raise TypeError(
|
||||
f"Image upload read() returned {type(content).__name__}, expected bytes"
|
||||
)
|
||||
with open(target_path, "wb") as f:
|
||||
f.write(content)
|
||||
return target_path
|
||||
|
||||
|
||||
async def _maybe_url_image(
|
||||
img_url: str,
|
||||
target_path: str,
|
||||
*,
|
||||
prefer_remote_source: bool = False,
|
||||
) -> str | None:
|
||||
if not isinstance(img_url, str):
|
||||
return None
|
||||
|
||||
if img_url.lower().startswith(("http://", "https://")):
|
||||
# Only bypass persistence when the caller explicitly disables input saves.
|
||||
# Otherwise keep the prefetch outside the measured server stages.
|
||||
if prefer_remote_source:
|
||||
return img_url
|
||||
# download image from URL and persist on disk
|
||||
input_path = await _save_url_image_to_path(img_url, target_path)
|
||||
return input_path
|
||||
elif img_url.startswith("data:image"):
|
||||
if prefer_remote_source:
|
||||
return img_url
|
||||
# encode image base64 url and persist on disk
|
||||
input_path = save_base64_image_to_path(img_url, target_path)
|
||||
return input_path
|
||||
else:
|
||||
raise ValueError("Unsupported image url format")
|
||||
|
||||
|
||||
async def _save_url_image_to_path(image_url: str, target_path: str) -> str:
|
||||
"""Download image from URL and save to target path."""
|
||||
|
||||
def _is_retryable_download_error(error: Exception) -> bool:
|
||||
if isinstance(error, httpx.HTTPStatusError):
|
||||
status_code = error.response.status_code
|
||||
# Retry on rate limit and transient server-side failures.
|
||||
return status_code == 429 or 500 <= status_code < 600
|
||||
# Retry on transient network/protocol issues.
|
||||
return isinstance(
|
||||
error,
|
||||
(
|
||||
httpx.TimeoutException,
|
||||
httpx.NetworkError,
|
||||
httpx.RemoteProtocolError,
|
||||
),
|
||||
)
|
||||
|
||||
os.makedirs(os.path.dirname(target_path), exist_ok=True)
|
||||
|
||||
max_attempts = 3
|
||||
backoff_seconds = 0.2
|
||||
last_error: Exception | None = None
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(follow_redirects=True) as client:
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
try:
|
||||
response = await client.get(image_url, timeout=10.0)
|
||||
response.raise_for_status()
|
||||
|
||||
# Determine file extension from content type or URL after downloading
|
||||
if not os.path.splitext(target_path)[1]:
|
||||
content_type = response.headers.get("content-type", "").lower()
|
||||
|
||||
url_path = image_url.split("?")[0]
|
||||
_, url_ext = os.path.splitext(url_path)
|
||||
url_ext = url_ext.lower()
|
||||
|
||||
if url_ext in {
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".png",
|
||||
".webp",
|
||||
".gif",
|
||||
".bmp",
|
||||
}:
|
||||
ext = ".jpg" if url_ext == ".jpeg" else url_ext
|
||||
elif content_type.startswith("image/"):
|
||||
if "jpeg" in content_type or "jpg" in content_type:
|
||||
ext = ".jpg"
|
||||
elif "png" in content_type:
|
||||
ext = ".png"
|
||||
elif "webp" in content_type:
|
||||
ext = ".webp"
|
||||
else:
|
||||
ext = ".jpg" # Default to jpg
|
||||
elif content_type == "application/octet-stream":
|
||||
# for octet-stream, if we couldn't get it from URL, default to jpg
|
||||
ext = ".jpg"
|
||||
else:
|
||||
raise ValueError(
|
||||
f"URL does not point to an image. Content-Type: {content_type}"
|
||||
)
|
||||
target_path = f"{target_path}{ext}"
|
||||
|
||||
with open(target_path, "wb") as f:
|
||||
f.write(response.content)
|
||||
|
||||
return target_path
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
if attempt == max_attempts or not _is_retryable_download_error(e):
|
||||
raise
|
||||
wait_s = backoff_seconds * (2 ** (attempt - 1))
|
||||
logger.warning(
|
||||
"Retrying image download (%s/%s) for %s after %.1fs due to: %s",
|
||||
attempt,
|
||||
max_attempts,
|
||||
image_url,
|
||||
wait_s,
|
||||
e,
|
||||
)
|
||||
await asyncio.sleep(wait_s)
|
||||
except Exception as e:
|
||||
final_error = last_error or e
|
||||
raise Exception(
|
||||
f"Failed to download image from URL {image_url}: {str(final_error)}"
|
||||
)
|
||||
|
||||
|
||||
async def process_generation_batch(
|
||||
scheduler_client: AsyncSchedulerClient,
|
||||
batch,
|
||||
) -> tuple[list[str], OutputBatch]:
|
||||
total_start_time = time.perf_counter()
|
||||
with trace_req(batch.trace_ctx), log_generation_timer(logger, batch.prompt):
|
||||
result = await scheduler_client.forward([batch])
|
||||
|
||||
if (
|
||||
result.output is None
|
||||
and result.output_file_paths is None
|
||||
and result.raw_frame_batches is None
|
||||
):
|
||||
error_msg = result.error or "Unknown error"
|
||||
raise RuntimeError(
|
||||
f"Model generation returned no output. Error from scheduler: {error_msg}"
|
||||
)
|
||||
|
||||
save_file_path_list = []
|
||||
if result.output_file_paths:
|
||||
save_file_path_list = result.output_file_paths
|
||||
elif result.output is not None:
|
||||
num_outputs = len(result.output)
|
||||
save_file_path_list = save_outputs(
|
||||
result.output,
|
||||
batch.data_type,
|
||||
batch.fps,
|
||||
batch.save_output,
|
||||
lambda idx: str(batch.output_file_path(num_outputs, idx)),
|
||||
audio=result.audio,
|
||||
audio_sample_rate=result.audio_sample_rate,
|
||||
output_compression=batch.output_compression,
|
||||
enable_frame_interpolation=batch.enable_frame_interpolation,
|
||||
frame_interpolation_exp=batch.frame_interpolation_exp,
|
||||
frame_interpolation_scale=batch.frame_interpolation_scale,
|
||||
frame_interpolation_model_path=batch.frame_interpolation_model_path,
|
||||
enable_upscaling=batch.enable_upscaling,
|
||||
upscaling_model_path=batch.upscaling_model_path,
|
||||
upscaling_scale=batch.upscaling_scale,
|
||||
)
|
||||
|
||||
total_time = time.perf_counter() - total_start_time
|
||||
if get_global_server_args().batching_max_size > 1:
|
||||
log_batch_completion(
|
||||
logger,
|
||||
len(save_file_path_list),
|
||||
total_time,
|
||||
)
|
||||
|
||||
if result.peak_memory_mb and result.peak_memory_mb > 0:
|
||||
logger.info(f"Peak memory usage: {result.peak_memory_mb:.2f} MB")
|
||||
|
||||
return save_file_path_list, result
|
||||
|
||||
|
||||
def merge_image_input_list(*inputs: Union[List, Any, None]) -> List:
|
||||
"""
|
||||
Merge multiple image input sources into a single list.
|
||||
|
||||
This function handles both single items and lists of items, merging them
|
||||
into a single flattened list. Useful for processing images, URLs, or other
|
||||
multimedia inputs that can come as either single items or lists.
|
||||
|
||||
Args:
|
||||
*inputs: Variable number of inputs, each can be None, single item, or list
|
||||
|
||||
Returns:
|
||||
List: Flattened list of all non-None inputs
|
||||
|
||||
Example:
|
||||
>>> merge_image_input_list(["img1", "img2"], "img3", None)
|
||||
["img1", "img2", "img3"]
|
||||
"""
|
||||
result = []
|
||||
for input_item in inputs:
|
||||
if input_item is not None:
|
||||
if isinstance(input_item, list):
|
||||
result.extend(input_item)
|
||||
else:
|
||||
result.append(input_item)
|
||||
return result
|
||||
|
||||
|
||||
def add_common_data_to_response(
|
||||
response: dict, request_id: str, result: OutputBatch
|
||||
) -> dict:
|
||||
if result.peak_memory_mb and result.peak_memory_mb > 0:
|
||||
response["peak_memory_mb"] = result.peak_memory_mb
|
||||
|
||||
if result.metrics and result.metrics.total_duration_s > 0:
|
||||
response["inference_time_s"] = result.metrics.total_duration_s
|
||||
|
||||
response["id"] = request_id
|
||||
|
||||
if result.action_pred is not None:
|
||||
t = result.action_pred
|
||||
response["action"] = {
|
||||
"data": t.tolist(),
|
||||
"shape": list(t.shape),
|
||||
"dtype": str(t.dtype).replace("torch.", ""),
|
||||
"raw_action_dim": result.action_raw_action_dim,
|
||||
"action_mode": result.action_mode,
|
||||
"domain_id": result.action_domain_id,
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def adjust_output_quality(output_quality: str, data_type: DataType = None) -> int:
|
||||
if output_quality == "default":
|
||||
return 50 if data_type == DataType.VIDEO else 75
|
||||
return OUTPUT_QUALITY_MAPPER.get(output_quality, None)
|
||||
Reference in New Issue
Block a user