[CI][Perf] migrate diffusion DFX benches to vllm bench serve --omni (#7737)
Signed-off-by: wangyu <410167048@qq.com>
This commit is contained in:
1 parent
a4ad73e08b
commit
0570b0da7e
30 files changed
+2040
-729
No files matched your search
@@ -19,6 +19,7 @@ from dataclasses import dataclass, replace
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import aiohttp
|
||||
import numpy as np
|
||||
@@ -873,6 +874,9 @@ class MixRequestFuncOutput(RequestFuncOutput):
|
||||
tts_turn_pcm_bytes: list[bytes] | None = None
|
||||
#: Per-stage snapshot from orchestrator ``metrics["stage_metrics"]`` (merged across SSE chunks).
|
||||
stage_metrics: dict[str, dict] | None = None
|
||||
#: Diffusion pipeline profiler timings from response ``stage_durations``
|
||||
#: (e.g. diffuse / text_encoder.forward / vae.decode), when present.
|
||||
stage_durations: dict[str, float] | None = None
|
||||
stage_id: int | None = None
|
||||
final_output_type: str | None = None
|
||||
duplex_request_metrics: list[dict[str, object]] | None = None
|
||||
@@ -883,6 +887,7 @@ _IMAGE_EDITS_EXTRA_BODY_FORM_FIELDS = (
|
||||
"negative_prompt",
|
||||
"num_inference_steps",
|
||||
"guidance_scale",
|
||||
"guidance_scale_2",
|
||||
"strength",
|
||||
"true_cfg_scale",
|
||||
"seed",
|
||||
@@ -902,15 +907,51 @@ def _guess_mime_type(path: str) -> str:
|
||||
return mime or "application/octet-stream"
|
||||
|
||||
|
||||
def _iter_image_edit_inputs(value: Any) -> Iterable[Any]:
|
||||
"""Yield image references from benchmark multimodal content."""
|
||||
_IMAGE_REFERENCE_SUFFIXES = frozenset({".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".heic", ".heif"})
|
||||
_VIDEO_REFERENCE_SUFFIXES = frozenset({".mp4", ".mov", ".webm", ".mkv", ".m4v"})
|
||||
|
||||
|
||||
def _string_reference_kind(reference: str) -> str | None:
|
||||
"""Classify a bare reference string as image, video, or file.
|
||||
|
||||
``data:image`` / ``data:video`` carry their type in the URL. Bare http(s)
|
||||
URLs use the path extension. An existing local path is a file upload, not
|
||||
an image or a video URL.
|
||||
"""
|
||||
if reference.startswith("data:image"):
|
||||
return "image"
|
||||
if reference.startswith("data:video"):
|
||||
return "video"
|
||||
if reference.startswith(("http://", "https://")):
|
||||
suffix = Path(urlparse(reference).path).suffix.lower()
|
||||
if suffix in _VIDEO_REFERENCE_SUFFIXES:
|
||||
return "video"
|
||||
if suffix in _IMAGE_REFERENCE_SUFFIXES:
|
||||
return "image"
|
||||
return None
|
||||
local_path = reference.removeprefix("file://")
|
||||
if local_path and os.path.exists(local_path):
|
||||
return "file"
|
||||
return None
|
||||
|
||||
|
||||
def _iter_image_reference_inputs(value: Any) -> Iterable[Any]:
|
||||
"""Yield image references from benchmark multimodal content.
|
||||
|
||||
``random-mm`` image buckets arrive as OpenAI chat parts
|
||||
``{"type": "image_url", "image_url": {"url": ...}}``. Yield
|
||||
``{"image_url": url}`` so the form helper keeps an explicit image type.
|
||||
Bare video strings are left for ``_iter_video_reference_inputs``.
|
||||
"""
|
||||
if value is None:
|
||||
return
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
yield from _iter_image_edit_inputs(item)
|
||||
yield from _iter_image_reference_inputs(item)
|
||||
return
|
||||
if not isinstance(value, dict):
|
||||
if isinstance(value, str) and _string_reference_kind(value) == "video":
|
||||
return
|
||||
yield value
|
||||
return
|
||||
|
||||
@@ -919,15 +960,49 @@ def _iter_image_edit_inputs(value: Any) -> Iterable[Any]:
|
||||
image_url = value.get("image_url")
|
||||
if isinstance(image_url, dict):
|
||||
url = image_url.get("url")
|
||||
if url:
|
||||
yield url
|
||||
elif image_url:
|
||||
yield image_url
|
||||
if isinstance(url, str) and url:
|
||||
yield {"image_url": url}
|
||||
elif isinstance(image_url, str) and image_url:
|
||||
yield {"image_url": image_url}
|
||||
return
|
||||
|
||||
for key in ("image", "images"):
|
||||
if key in value:
|
||||
yield from _iter_image_edit_inputs(value[key])
|
||||
yield from _iter_image_reference_inputs(value[key])
|
||||
|
||||
|
||||
def _iter_video_reference_inputs(value: Any) -> Iterable[dict[str, str]]:
|
||||
"""Yield structured video references from benchmark multimodal content.
|
||||
|
||||
``random-mm`` video buckets arrive as OpenAI chat parts
|
||||
``{"type": "video_url", "video_url": {"url": ...}}``, or as a bare
|
||||
``data:video`` / video http(s) string. Yield ``{"video_url": url}`` so the
|
||||
form helper keeps the video branch.
|
||||
"""
|
||||
if value is None:
|
||||
return
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
yield from _iter_video_reference_inputs(item)
|
||||
return
|
||||
if not isinstance(value, dict):
|
||||
if isinstance(value, str) and _string_reference_kind(value) == "video":
|
||||
yield {"video_url": value}
|
||||
return
|
||||
|
||||
if value.get("type") == "video_url":
|
||||
video_url = value.get("video_url")
|
||||
if isinstance(video_url, dict):
|
||||
url = video_url.get("url")
|
||||
if isinstance(url, str) and url:
|
||||
yield {"video_url": url}
|
||||
elif isinstance(video_url, str) and video_url:
|
||||
yield {"video_url": video_url}
|
||||
return
|
||||
|
||||
for key in ("video", "videos"):
|
||||
if key in value:
|
||||
yield from _iter_video_reference_inputs(value[key])
|
||||
|
||||
|
||||
def _add_image_edit_input_to_form(form: aiohttp.FormData, image_input: Any) -> None:
|
||||
@@ -940,6 +1015,12 @@ def _add_image_edit_input_to_form(form: aiohttp.FormData, image_input: Any) -> N
|
||||
)
|
||||
return
|
||||
|
||||
if isinstance(image_input, Mapping) and _is_structured_image_reference(image_input):
|
||||
image_url = image_input.get("image_url")
|
||||
if isinstance(image_url, str) and image_url:
|
||||
_add_image_edit_input_to_form(form, image_url)
|
||||
return
|
||||
|
||||
if isinstance(image_input, str):
|
||||
if image_input.startswith(("data:image", "http://", "https://")):
|
||||
form.add_field("url", image_input)
|
||||
@@ -1137,6 +1218,68 @@ def _update_output_peak_memory_from_payload(output: MixRequestFuncOutput, data:
|
||||
output.peak_memory_mb = peak_memory_mb
|
||||
|
||||
|
||||
def _coerce_stage_durations_dict(raw: object) -> dict[str, float] | None:
|
||||
"""Normalize a stage_durations mapping to ``dict[str, float]``."""
|
||||
if not isinstance(raw, dict) or not raw:
|
||||
return None
|
||||
coerced: dict[str, float] = {}
|
||||
for key, value in raw.items():
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)) or not np.isfinite(value):
|
||||
continue
|
||||
coerced[str(key)] = float(value)
|
||||
return coerced or None
|
||||
|
||||
|
||||
def _extract_stage_durations_from_payload(data: Mapping[str, object]) -> dict[str, float] | None:
|
||||
"""Pull pipeline profiler timings from video/image/chat response shapes."""
|
||||
found = _coerce_stage_durations_dict(data.get("stage_durations"))
|
||||
if found:
|
||||
return found
|
||||
|
||||
metrics = data.get("metrics")
|
||||
if isinstance(metrics, dict):
|
||||
found = _coerce_stage_durations_dict(metrics.get("stage_durations"))
|
||||
if found:
|
||||
return found
|
||||
|
||||
response_data = data.get("data")
|
||||
if isinstance(response_data, list):
|
||||
for item in response_data:
|
||||
if isinstance(item, dict):
|
||||
found = _coerce_stage_durations_dict(item.get("stage_durations"))
|
||||
if found:
|
||||
return found
|
||||
|
||||
choices = data.get("choices")
|
||||
if isinstance(choices, list):
|
||||
for choice in choices:
|
||||
if not isinstance(choice, dict):
|
||||
continue
|
||||
for message_key in ("message", "delta"):
|
||||
message = choice.get(message_key)
|
||||
if not isinstance(message, dict):
|
||||
continue
|
||||
content = message.get("content")
|
||||
if isinstance(content, list):
|
||||
for item in content:
|
||||
if isinstance(item, dict):
|
||||
found = _coerce_stage_durations_dict(item.get("stage_durations"))
|
||||
if found:
|
||||
return found
|
||||
elif isinstance(content, dict):
|
||||
found = _coerce_stage_durations_dict(content.get("stage_durations"))
|
||||
if found:
|
||||
return found
|
||||
return None
|
||||
|
||||
|
||||
def _update_output_stage_durations_from_payload(output: MixRequestFuncOutput, data: Mapping[str, object]) -> None:
|
||||
"""Persist the full profiler ``stage_durations`` map when the response has one."""
|
||||
found = _extract_stage_durations_from_payload(data)
|
||||
if found:
|
||||
output.stage_durations = found
|
||||
|
||||
|
||||
def _image_metrics_from_stage_metrics(metrics: object) -> tuple[int, float, int, float]:
|
||||
if not isinstance(metrics, dict):
|
||||
return 0, 0.0, 0, 0.0
|
||||
@@ -1209,6 +1352,7 @@ def _apply_image_metrics_from_payload(output: MixRequestFuncOutput, data: Mappin
|
||||
"""Populate image benchmark fields from an OpenAI-compatible image payload."""
|
||||
_update_output_stage_metrics_from_payload(output, data, update_output_tokens=False)
|
||||
_update_output_peak_memory_from_payload(output, data)
|
||||
_update_output_stage_durations_from_payload(output, data)
|
||||
|
||||
payload_image_count = 0
|
||||
response_data = data.get("data")
|
||||
@@ -1321,15 +1465,129 @@ def _video_frames_from_payload(data: Mapping[str, object], request_body: Mapping
|
||||
|
||||
|
||||
def _is_structured_image_reference(reference: Mapping[str, object]) -> bool:
|
||||
"""True for API image_reference objects ({image_url}/{file_id})."""
|
||||
"""True for API image_reference objects ({"image_url": "..."})."""
|
||||
image_url = reference.get("image_url")
|
||||
file_id = reference.get("file_id")
|
||||
has_url = isinstance(image_url, str) and bool(image_url)
|
||||
has_file_id = isinstance(file_id, str) and bool(file_id)
|
||||
return has_url or has_file_id
|
||||
return isinstance(image_url, str) and bool(image_url)
|
||||
|
||||
|
||||
def _add_video_reference_to_form(form: aiohttp.FormData, reference: object) -> bool:
|
||||
def _is_structured_video_reference(reference: Mapping[str, object]) -> bool:
|
||||
"""True for API video_reference objects ({"video_url": "..."})."""
|
||||
video_url = reference.get("video_url")
|
||||
return isinstance(video_url, str) and bool(video_url)
|
||||
|
||||
|
||||
_VIDEO_REFERENCE_JSON_MAX_BYTES = 1024 * 1024
|
||||
|
||||
|
||||
def _data_video_json_exceeds_text_limit(video_url: str) -> bool:
|
||||
"""True when a data:video URL would exceed the ~1MB multipart text-part limit."""
|
||||
if not video_url.startswith("data:video"):
|
||||
return False
|
||||
encoded = json.dumps({"video_url": video_url}).encode("utf-8")
|
||||
return len(encoded) > _VIDEO_REFERENCE_JSON_MAX_BYTES
|
||||
|
||||
|
||||
def _add_data_video_upload(form: aiohttp.FormData, video_url: str) -> bool:
|
||||
"""Upload one inline video as ``input_references`` instead of a JSON text part."""
|
||||
header, _, payload = video_url.partition(",")
|
||||
if not payload:
|
||||
raise ValueError(f"Unsupported video data URL: {video_url[:64]!r}")
|
||||
try:
|
||||
video_bytes = base64.b64decode(payload)
|
||||
except (ValueError, TypeError) as exc:
|
||||
raise ValueError("video data URL is not valid base64") from exc
|
||||
mime = header[len("data:") :].split(";", 1)[0] or "video/mp4"
|
||||
suffix = ".mp4" if mime.endswith("mp4") else ".bin"
|
||||
form.add_field(
|
||||
"input_references",
|
||||
video_bytes,
|
||||
filename=f"benchmark-reference{suffix}",
|
||||
content_type=mime,
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def _file_bytes_as_data_url(raw: bytes, mime: str) -> str:
|
||||
encoded = base64.b64encode(raw).decode("ascii")
|
||||
return f"data:{mime};base64,{encoded}"
|
||||
|
||||
|
||||
def _image_reference_json_value(reference: object) -> object:
|
||||
"""Turn an image file into a JSON ``image_reference`` when it must share a form.
|
||||
|
||||
``input_reference`` cannot be combined with ``video_reference``. Image URLs
|
||||
stay URLs. Upload bytes and local image files become ``data:image`` URLs.
|
||||
"""
|
||||
if isinstance(reference, Mapping) and "bytes" in reference and not _is_structured_image_reference(reference):
|
||||
raw = reference["bytes"]
|
||||
if not isinstance(raw, (bytes, bytearray)):
|
||||
raise ValueError(f"image reference bytes must be bytes (got {type(raw).__name__}).")
|
||||
content_type = reference.get("content_type", "image/png")
|
||||
if not isinstance(content_type, str) or not content_type.startswith("image/"):
|
||||
content_type = "image/png"
|
||||
return {"image_url": _file_bytes_as_data_url(bytes(raw), content_type.split(";", 1)[0])}
|
||||
if isinstance(reference, str):
|
||||
kind = _string_reference_kind(reference)
|
||||
if kind == "image":
|
||||
return {"image_url": reference}
|
||||
if kind == "file":
|
||||
local_path = reference.removeprefix("file://")
|
||||
mime = _guess_mime_type(local_path)
|
||||
if not mime.startswith("image/"):
|
||||
mime = "image/png"
|
||||
with open(local_path, "rb") as handle:
|
||||
return {"image_url": _file_bytes_as_data_url(handle.read(), mime)}
|
||||
return reference
|
||||
|
||||
|
||||
def _video_reference_json_value(reference: object) -> object:
|
||||
"""Turn a video file into a JSON ``video_reference`` when it must share a form.
|
||||
|
||||
``input_reference`` cannot be combined with ``image_reference``. Video URLs
|
||||
stay URLs. Local video files become ``data:video`` URLs.
|
||||
"""
|
||||
if isinstance(reference, Mapping) and "bytes" in reference and not _is_structured_video_reference(reference):
|
||||
raw = reference["bytes"]
|
||||
if not isinstance(raw, (bytes, bytearray)):
|
||||
raise ValueError(f"video reference bytes must be bytes (got {type(raw).__name__}).")
|
||||
content_type = reference.get("content_type", "video/mp4")
|
||||
if not isinstance(content_type, str) or not content_type.startswith("video/"):
|
||||
content_type = "video/mp4"
|
||||
return {"video_url": _file_bytes_as_data_url(bytes(raw), content_type.split(";", 1)[0])}
|
||||
if isinstance(reference, str):
|
||||
kind = _string_reference_kind(reference)
|
||||
if kind == "video":
|
||||
return {"video_url": reference}
|
||||
if kind == "file":
|
||||
local_path = reference.removeprefix("file://")
|
||||
mime = _guess_mime_type(local_path)
|
||||
if not mime.startswith("video/"):
|
||||
mime = "video/mp4"
|
||||
with open(local_path, "rb") as handle:
|
||||
return {"video_url": _file_bytes_as_data_url(handle.read(), mime)}
|
||||
return reference
|
||||
|
||||
|
||||
def _add_video_reference_to_form(
|
||||
form: aiohttp.FormData,
|
||||
reference: object,
|
||||
*,
|
||||
upload_inline_video: bool = True,
|
||||
) -> bool:
|
||||
"""Encode one reference: image URL, video URL, or file upload.
|
||||
|
||||
Image URLs use ``image_reference``. Video URLs use ``video_reference``.
|
||||
A lone ``data:video`` whose JSON text exceeds 1MB is uploaded as
|
||||
``input_references``. Local paths and raw bytes use ``input_reference``.
|
||||
``upload_inline_video`` must be false when an image is on the same form:
|
||||
``input_references`` cannot be combined with ``image_reference``.
|
||||
"""
|
||||
candidates = reference if isinstance(reference, list) else [reference]
|
||||
for item in candidates:
|
||||
if isinstance(item, Mapping):
|
||||
file_id = item.get("file_id")
|
||||
if isinstance(file_id, str) and file_id:
|
||||
raise ValueError("file_id is not supported yet")
|
||||
if isinstance(reference, dict) and "bytes" in reference:
|
||||
form.add_field(
|
||||
"input_reference",
|
||||
@@ -1343,23 +1601,39 @@ def _add_video_reference_to_form(form: aiohttp.FormData, reference: object) -> b
|
||||
form.add_field("image_reference", json.dumps(dict(reference)))
|
||||
return True
|
||||
|
||||
if isinstance(reference, Mapping) and _is_structured_video_reference(reference):
|
||||
video_url = reference.get("video_url")
|
||||
if upload_inline_video and isinstance(video_url, str) and _data_video_json_exceeds_text_limit(video_url):
|
||||
return _add_data_video_upload(form, video_url)
|
||||
form.add_field("video_reference", json.dumps(dict(reference)))
|
||||
return True
|
||||
|
||||
if isinstance(reference, list):
|
||||
if reference and all(isinstance(item, Mapping) and _is_structured_image_reference(item) for item in reference):
|
||||
form.add_field("image_reference", json.dumps([dict(item) for item in reference]))
|
||||
return True
|
||||
if reference and all(isinstance(item, Mapping) and _is_structured_video_reference(item) for item in reference):
|
||||
form.add_field("video_reference", json.dumps([dict(item) for item in reference]))
|
||||
return True
|
||||
raise ValueError(
|
||||
"Unsupported image_reference list; expected non-empty list of "
|
||||
'{"image_url": "..."} and/or {"file_id": "..."} objects.'
|
||||
"Unsupported reference list; expected non-empty list of "
|
||||
'{"image_url": "..."} or {"video_url": "..."} objects.'
|
||||
)
|
||||
|
||||
if isinstance(reference, str):
|
||||
if reference.startswith(("data:image", "http://", "https://")):
|
||||
kind = _string_reference_kind(reference)
|
||||
if kind == "image":
|
||||
form.add_field("image_reference", json.dumps({"image_url": reference}))
|
||||
return True
|
||||
local_path = reference.removeprefix("file://")
|
||||
if os.path.exists(local_path):
|
||||
with open(local_path, "rb") as f:
|
||||
reference_bytes = f.read()
|
||||
if kind == "video":
|
||||
if upload_inline_video and _data_video_json_exceeds_text_limit(reference):
|
||||
return _add_data_video_upload(form, reference)
|
||||
form.add_field("video_reference", json.dumps({"video_url": reference}))
|
||||
return True
|
||||
if kind == "file":
|
||||
local_path = reference.removeprefix("file://")
|
||||
with open(local_path, "rb") as handle:
|
||||
reference_bytes = handle.read()
|
||||
form.add_field(
|
||||
"input_reference",
|
||||
reference_bytes,
|
||||
@@ -1367,15 +1641,64 @@ def _add_video_reference_to_form(form: aiohttp.FormData, reference: object) -> b
|
||||
content_type=_guess_mime_type(local_path),
|
||||
)
|
||||
return True
|
||||
raise ValueError(f"Unsupported image_reference path or URL: {reference!r}")
|
||||
if reference.startswith(("http://", "https://")):
|
||||
raise ValueError(
|
||||
"Bare http(s) reference needs an image or video extension "
|
||||
f"({', '.join(sorted(_IMAGE_REFERENCE_SUFFIXES | _VIDEO_REFERENCE_SUFFIXES))}); "
|
||||
f"got {reference!r}."
|
||||
)
|
||||
raise ValueError(f"Unsupported reference path or URL: {reference!r}")
|
||||
|
||||
raise ValueError(
|
||||
"Unsupported image_reference; expected upload bytes, local path/URL string, "
|
||||
'or {"image_url": "..."} / {"file_id": "..."} object '
|
||||
"Unsupported reference; expected image URL, video URL, upload bytes, or a local file "
|
||||
f"(got {type(reference).__name__})."
|
||||
)
|
||||
|
||||
|
||||
def _add_combined_video_form_references(
|
||||
form: aiohttp.FormData,
|
||||
multi_modal_content: Any,
|
||||
extra_body: Mapping[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""Serialize image and video refs using a server-accepted field pair.
|
||||
|
||||
Alone, each reference uses its own field: image URL → ``image_reference``,
|
||||
video URL → ``video_reference``, file → ``input_reference``. A lone inline
|
||||
video whose JSON text exceeds 1MB is uploaded as ``input_references``.
|
||||
Together, that upload cannot be combined with ``image_reference``, so both
|
||||
sides stay on the JSON fields. A file paired with the other media is rewritten
|
||||
as a data URL of the matching type.
|
||||
"""
|
||||
extra_body = extra_body or {}
|
||||
image_refs = list(_iter_image_reference_inputs(multi_modal_content))
|
||||
video_refs = list(_iter_video_reference_inputs(multi_modal_content))
|
||||
if not image_refs and extra_body.get("image_reference") is not None:
|
||||
image_refs = [extra_body["image_reference"]]
|
||||
if not video_refs and extra_body.get("video_reference") is not None:
|
||||
video_refs = [extra_body["video_reference"]]
|
||||
|
||||
if image_refs and video_refs:
|
||||
for raw in (image_refs[0], video_refs[0]):
|
||||
candidates = raw if isinstance(raw, list) else [raw]
|
||||
for item in candidates:
|
||||
if isinstance(item, Mapping):
|
||||
file_id = item.get("file_id")
|
||||
if isinstance(file_id, str) and file_id:
|
||||
raise ValueError("file_id is not supported yet")
|
||||
_add_video_reference_to_form(form, _image_reference_json_value(image_refs[0]))
|
||||
_add_video_reference_to_form(
|
||||
form,
|
||||
_video_reference_json_value(video_refs[0]),
|
||||
upload_inline_video=False,
|
||||
)
|
||||
return
|
||||
|
||||
if image_refs:
|
||||
_add_video_reference_to_form(form, image_refs[0])
|
||||
if video_refs:
|
||||
_add_video_reference_to_form(form, video_refs[0])
|
||||
|
||||
|
||||
def _add_video_extra_body_to_form(
|
||||
form: aiohttp.FormData,
|
||||
extra_body: Mapping[str, object],
|
||||
@@ -1398,9 +1721,11 @@ def _add_video_extra_body_to_form(
|
||||
"height",
|
||||
"poll_interval_s",
|
||||
"poll_timeout_s",
|
||||
# Handled only by _add_video_reference_to_form (upload / JSON image_url).
|
||||
# Handled only by _add_video_reference_to_form (upload / JSON image_url / video_url).
|
||||
"image_reference",
|
||||
"video_reference",
|
||||
"input_reference",
|
||||
"input_references",
|
||||
*_VIDEO_FORM_FIELDS,
|
||||
}
|
||||
for key, value in extra_body.items():
|
||||
@@ -1421,8 +1746,9 @@ def _apply_video_metrics_from_payload(
|
||||
output.video_frames = _video_frames_from_payload(data, request_body)
|
||||
_update_output_stage_metrics_from_payload(output, data, update_output_tokens=False)
|
||||
_update_output_peak_memory_from_payload(output, data)
|
||||
_update_output_stage_durations_from_payload(output, data)
|
||||
|
||||
stage_durations = data.get("stage_durations")
|
||||
stage_durations = output.stage_durations if output.stage_durations is not None else data.get("stage_durations")
|
||||
stage_gen_ms = _video_generation_ms_from_stage_durations(stage_durations)
|
||||
if stage_gen_ms <= 0:
|
||||
inference_time_s = coerce_positive_float_scalar(data.get("inference_time_s"))
|
||||
@@ -1534,6 +1860,7 @@ async def async_request_openai_chat_omni_completions(
|
||||
output.image_pixels = 0
|
||||
output.denoise_step_latency_ms = 0.0
|
||||
output.peak_memory_mb = 0.0
|
||||
output.stage_durations = None
|
||||
completion_tokens_seen = 0
|
||||
try:
|
||||
async with session.post(url=api_url, json=payload, headers=headers) as response:
|
||||
@@ -1564,6 +1891,7 @@ async def async_request_openai_chat_omni_completions(
|
||||
data = json.loads(chunk)
|
||||
_update_output_stage_metrics_from_payload(output, data)
|
||||
_update_output_peak_memory_from_payload(output, data)
|
||||
_update_output_stage_durations_from_payload(output, data)
|
||||
usage = data.get("usage")
|
||||
completion_tokens = None
|
||||
if isinstance(usage, dict):
|
||||
@@ -1926,15 +2254,7 @@ async def async_request_openai_videos_omni(
|
||||
form.add_field("size", str(request_body["size"]))
|
||||
_add_video_extra_body_to_form(form, extra_body, request_body)
|
||||
|
||||
reference_added = False
|
||||
for reference in _iter_image_edit_inputs(request_func_input.multi_modal_content):
|
||||
if _add_video_reference_to_form(form, reference):
|
||||
reference_added = True
|
||||
break
|
||||
if not reference_added:
|
||||
image_reference = extra_body.get("image_reference")
|
||||
if image_reference is not None:
|
||||
_add_video_reference_to_form(form, image_reference)
|
||||
_add_combined_video_form_references(form, request_func_input.multi_modal_content, extra_body)
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}",
|
||||
@@ -2044,7 +2364,7 @@ async def async_request_openai_image_edits_omni(
|
||||
_add_image_edit_extra_body_to_form(form, extra_body)
|
||||
|
||||
try:
|
||||
image_inputs = list(_iter_image_edit_inputs(request_func_input.multi_modal_content))
|
||||
image_inputs = list(_iter_image_reference_inputs(request_func_input.multi_modal_content))
|
||||
if not image_inputs:
|
||||
raise ValueError(
|
||||
"openai-image-edits-omni requires image multimodal content. "
|
||||
@@ -2102,6 +2422,7 @@ async def async_request_openai_image_edits_omni(
|
||||
update_output_tokens=(data.get("type") == "ar_delta"),
|
||||
)
|
||||
_update_output_peak_memory_from_payload(output, data)
|
||||
_update_output_stage_durations_from_payload(output, data)
|
||||
|
||||
chunk_type = data.get("type")
|
||||
if chunk_type == "ar_delta":
|
||||
@@ -3201,6 +3522,9 @@ async def benchmark(
|
||||
defs.MEAN_PEAK_MEMORY_MB: getattr(mm_metrics, defs.MEAN_PEAK_MEMORY_MB),
|
||||
defs.MEDIAN_PEAK_MEMORY_MB: getattr(mm_metrics, defs.MEDIAN_PEAK_MEMORY_MB),
|
||||
defs.PERCENTILES_PEAK_MEMORY_MB: getattr(mm_metrics, defs.PERCENTILES_PEAK_MEMORY_MB),
|
||||
defs.STAGE_DURATIONS_MEAN: getattr(mm_metrics, defs.STAGE_DURATIONS_MEAN) or {},
|
||||
defs.STAGE_DURATIONS_P50: getattr(mm_metrics, defs.STAGE_DURATIONS_P50) or {},
|
||||
defs.STAGE_DURATIONS_P99: getattr(mm_metrics, defs.STAGE_DURATIONS_P99) or {},
|
||||
"input_lens": [output.prompt_len for output in outputs],
|
||||
"start_times": [output.start_time for output in outputs],
|
||||
"output_lens": actual_output_lens,
|
||||
|
||||
Reference in new issue
Block a user