547 lines
23 KiB
Python
547 lines
23 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
|
|
|
"""vLLM-Omni extensions for the ``vllm bench serve`` CLI.
|
|
|
|
Core functions:
|
|
add_omni_args: Register all Omni-specific argument groups by calling the
|
|
feature-specific ``add_*_cli_args`` helpers in this module.
|
|
extend_omni_choices: Add Omni datasets and backends to choices defined by
|
|
the upstream vLLM parser, including its shadow parser.
|
|
update_omni_help: Extend upstream help text with Omni-specific behavior.
|
|
preprocess_serve_args: Apply transformations that require parsed values
|
|
before the serving benchmark starts.
|
|
|
|
``OmniBenchmarkServingSubcommand.add_cli_args`` invokes the first three after
|
|
upstream vLLM registers its arguments. New feature arguments should normally be
|
|
added to the corresponding ``add_*_cli_args`` helper, or to a new helper called
|
|
by ``add_omni_args``.
|
|
"""
|
|
|
|
import argparse
|
|
import math
|
|
from pathlib import Path
|
|
|
|
_DEFAULT_OMNIINTERACT_NUM_PROMPTS = 3
|
|
|
|
|
|
def _positive_finite_float(value: str) -> float:
|
|
parsed = float(value)
|
|
if not math.isfinite(parsed) or parsed <= 0:
|
|
raise argparse.ArgumentTypeError(f"must be a finite positive number, got {value!r}")
|
|
return parsed
|
|
|
|
|
|
def _positive_int(value: str) -> int:
|
|
parsed = int(value)
|
|
if parsed <= 0:
|
|
raise argparse.ArgumentTypeError(f"must be a positive integer, got {value!r}")
|
|
return parsed
|
|
|
|
|
|
def _existing_file(value: str) -> str:
|
|
path = Path(value).expanduser()
|
|
if not path.is_file():
|
|
raise argparse.ArgumentTypeError(f"file does not exist: {value!r}")
|
|
return str(path)
|
|
|
|
|
|
def add_omniinteract_cli_args(parser: argparse.ArgumentParser) -> None:
|
|
from vllm_omni.benchmarks.data_modules.omniinteract_dataset import (
|
|
DEFAULT_MAX_VIDEO_DURATION_S,
|
|
OMNIINTERACT_SCENARIO_TAGS,
|
|
OMNIINTERACT_SUBSETS,
|
|
)
|
|
|
|
group = parser.add_argument_group("OmniInteract Benchmark Options")
|
|
group.add_argument(
|
|
"--omniinteract-subsets", nargs="+", choices=OMNIINTERACT_SUBSETS, default=list(OMNIINTERACT_SUBSETS)
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-scenario-tags",
|
|
nargs="+",
|
|
choices=OMNIINTERACT_SCENARIO_TAGS,
|
|
default=None,
|
|
help=(
|
|
"Scenario tags used when sampling cases: realtime, proactive, nested, interrupted, 1qna. "
|
|
"Default behavior covers each requested tag with at least one case (when available), "
|
|
"then fills remaining --num-prompts from the rest of the selected subsets."
|
|
),
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-scenario-focus",
|
|
action="store_true",
|
|
help=(
|
|
"Only run cases matching --omniinteract-scenario-tags. "
|
|
"Without this flag, tags are used for coverage-first sampling."
|
|
),
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-video-list",
|
|
type=_existing_file,
|
|
help=(
|
|
"JSONL of sampled OmniInteract cases (video_path, output_name, subset), "
|
|
"typically sampled_cases.jsonl from a prior run. Preserves list order. "
|
|
"Cannot be combined with --seed, --dataset-path, or --omniinteract-scenario-tags. "
|
|
"With this flag, --num-prompts caps the prefix of the list (0 or oversized = all)."
|
|
),
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-timeout-s", type=_positive_finite_float, default=900.0, help="Complete session timeout."
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-media-timeout-s",
|
|
type=_positive_finite_float,
|
|
default=600.0,
|
|
help="Per-command media timeout.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-max-video-duration-s",
|
|
type=_positive_finite_float,
|
|
default=DEFAULT_MAX_VIDEO_DURATION_S,
|
|
help="Reject media longer than this safety limit before decoding.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-ref-audio", type=_existing_file, help="Reference WAV for native-duplex audio output."
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-require-response", action="store_true", help="Fail LISTEN-only functional E2E cases."
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-output-dir",
|
|
type=Path,
|
|
default=Path("omniinteract-output"),
|
|
help="Directory for case and evaluator artifacts.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-evaluate",
|
|
action="store_true",
|
|
help="Run the text-only OmniInteract judge after generation and print an accuracy report.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-judge-base-url",
|
|
default="http://127.0.0.1:8000",
|
|
help="Base URL of an already-running OpenAI-compatible judge server.",
|
|
)
|
|
group.add_argument("--omniinteract-judge-model", help="Model name exposed by the judge server.")
|
|
group.add_argument(
|
|
"--omniinteract-judge-api-key",
|
|
default="EMPTY",
|
|
help="Bearer token for the judge server.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-judge-timeout-s",
|
|
type=_positive_finite_float,
|
|
default=60.0,
|
|
help="Timeout for each judge request.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-judge-max-tokens",
|
|
type=_positive_int,
|
|
default=512,
|
|
help="Maximum completion tokens for each judge request.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-eval-workers",
|
|
type=_positive_int,
|
|
default=8,
|
|
help="Maximum number of concurrent judge requests.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-eval-output-dir",
|
|
type=Path,
|
|
help="Accuracy artifact directory; defaults to OUTPUT_DIR/evaluation.",
|
|
)
|
|
group.add_argument(
|
|
"--omniinteract-eval-skip-existing",
|
|
action="store_true",
|
|
help="Reuse successful per-case evaluation artifacts.",
|
|
)
|
|
|
|
|
|
def add_multi_stage_cli_args(parser: argparse.ArgumentParser) -> None:
|
|
"""Add CLI arguments for vLLM-Omni multi-stage benchmarks."""
|
|
group = parser.add_argument_group("vLLM-Omni Multi-stage Benchmark Options")
|
|
group.add_argument(
|
|
"--print-stage",
|
|
action="store_true",
|
|
default=False,
|
|
help=(
|
|
"Print per-stage benchmark metrics for --omni serving when stage metrics are returned by the server. "
|
|
"Disabled by default. The latency sections follow --percentile-metrics by modality: "
|
|
"ttft/tpot/itl control text stages, ttfc/tpoc/icl control internal stream stages, "
|
|
"and tpop controls both text TPOP and internal stream TPOP."
|
|
),
|
|
)
|
|
group.add_argument(
|
|
"--omni-request-timeout-s",
|
|
type=float,
|
|
default=None,
|
|
help=(
|
|
"Total per-request timeout in seconds for benchmark HTTP requests. Timed-out requests are "
|
|
"recorded as failed instead of stalling the run on a hung server. Defaults to 900 when "
|
|
"omitted; set <= 0 to restore the legacy 6 h cap."
|
|
),
|
|
)
|
|
|
|
|
|
def add_diffusion_cli_args(parser: argparse.ArgumentParser) -> None:
|
|
"""Add CLI arguments for diffusion model benchmarks."""
|
|
group = parser.add_argument_group("Diffusion Models Options")
|
|
group.add_argument(
|
|
"--image-edits-bot-task",
|
|
dest="bot_task",
|
|
type=str,
|
|
default="think",
|
|
help=(
|
|
"Default bot_task form field for image edits "
|
|
"(--backend openai-image-edits-omni or --endpoint /v1/images/edits). "
|
|
'Use --extra-body \'{"bot_task":"..."}\' to override per run.'
|
|
),
|
|
)
|
|
|
|
|
|
def add_daily_omni_cli_args(parser: argparse.ArgumentParser) -> None:
|
|
"""Add CLI arguments specific to the Daily-Omni dataset."""
|
|
group = parser.add_argument_group("Daily-Omni Dataset Options")
|
|
group.add_argument(
|
|
"--daily-omni-qa-json",
|
|
type=str,
|
|
default=None,
|
|
help="Path to local upstream qa.json. When set, QA rows are read from this file and "
|
|
"the HuggingFace dataset is not loaded (no network). Use with --daily-omni-video-dir "
|
|
"for fully offline runs. --dataset-path / Hub split flags are then ignored for QA loading.",
|
|
)
|
|
group.add_argument(
|
|
"--daily-omni-video-dir",
|
|
type=str,
|
|
default=None,
|
|
help="Root directory of extracted Daily-Omni videos (contents of Videos.tar: "
|
|
"each video_id in its own subdir with {video_id}_video.mp4). "
|
|
"If omitted, Videos.tar is downloaded from the Hugging Face dataset repo on first multimodal "
|
|
"request. "
|
|
"When using file URLs, you MUST start the vLLM server with "
|
|
"--allowed-local-media-path set to this same directory (or a parent), "
|
|
"otherwise requests fail with 'Cannot load local files without "
|
|
"--allowed-local-media-path'.",
|
|
)
|
|
group.add_argument(
|
|
"--daily-omni-inline-local-video",
|
|
action="store_true",
|
|
default=False,
|
|
help="For local videos only: embed MP4 as base64 data URLs in benchmark "
|
|
"requests so the server does not need --allowed-local-media-path. "
|
|
"Increases request size and client memory; use for small --num-prompts. "
|
|
"When using --daily-omni-input-mode audio or all, local WAV files are "
|
|
"embedded the same way.",
|
|
)
|
|
group.add_argument(
|
|
"--daily-omni-input-mode",
|
|
type=str,
|
|
choices=["all", "visual", "audio"],
|
|
default="all",
|
|
help="Daily-Omni input protocol (mirrors upstream Lliar-liar/Daily-Omni "
|
|
"--input_mode). 'visual': video only (default). 'audio': WAV only, "
|
|
"requires {video_id}/{video_id}_audio.wav under --daily-omni-video-dir. "
|
|
"'all': video + WAV together. Sets mm_processor_kwargs.use_audio_in_video=false "
|
|
"and matches official separate video/audio streams.",
|
|
)
|
|
group.add_argument(
|
|
"--daily-omni-pack-mode",
|
|
type=str,
|
|
choices=["qwen", "minicpm-interleave"],
|
|
default="qwen",
|
|
help="How to pack multimodal parts into OpenAI chat messages. "
|
|
"'qwen' (default): one video_url + one audio_url (Daily-Omni/Qwen protocol). "
|
|
"'minicpm-interleave': MiniCPM-o official recipe — 1fps frames interleaved with "
|
|
"matching 1s audio segments as image_url/audio_url pairs (needed to approach "
|
|
"OpenBMB ~80%% Daily-Omni; requires local Videos extract). By default this mode "
|
|
"writes JPEG/WAV segments under "
|
|
"<daily-omni-video-dir>/.minicpm_daily_omni_interleave and sends file:// URLs, "
|
|
"so start the server with --allowed-local-media-path <daily-omni-video-dir> "
|
|
"(or a parent). Alternatively pass --daily-omni-inline-local-video to embed "
|
|
"segments as data URLs. For MiniCPM-o 4.5 string chat templates also start the "
|
|
"server with --interleave-mm-strings.",
|
|
)
|
|
group.add_argument(
|
|
"--daily-omni-save-eval-items",
|
|
action="store_true",
|
|
default=False,
|
|
help="Include per-request Daily-Omni accuracy rows (gold/predicted/correct) "
|
|
"in the saved JSON under key daily_omni_eval_items. "
|
|
"Alternatively set env DAILY_OMNI_SAVE_EVAL_ITEMS=1.",
|
|
)
|
|
|
|
|
|
def add_videomme_cli_args(parser: argparse.ArgumentParser) -> None:
|
|
"""Add CLI arguments specific to the Video-MME dataset."""
|
|
group = parser.add_argument_group("Video-MME Dataset Options")
|
|
group.add_argument(
|
|
"--videomme-parquet",
|
|
type=str,
|
|
default=None,
|
|
help="Path to local Video-MME parquet "
|
|
"(e.g. videomme/test-00000-of-00001.parquet). When set, Hub QA loading is skipped.",
|
|
)
|
|
group.add_argument(
|
|
"--videomme-video-dir",
|
|
type=str,
|
|
default=None,
|
|
help="Directory containing extracted Video-MME videos (videoID.mp4). "
|
|
"Typical layout after unzipping videos_chunked_*.zip: <root>/video/. "
|
|
"When using file:// URLs, start the server with --allowed-local-media-path "
|
|
"covering this directory.",
|
|
)
|
|
group.add_argument(
|
|
"--videomme-subtitle-dir",
|
|
type=str,
|
|
default=None,
|
|
help="Directory containing Video-MME .srt subtitles (videoID.srt). Used only with --videomme-use-subtitle.",
|
|
)
|
|
group.add_argument(
|
|
"--videomme-pack-mode",
|
|
type=str,
|
|
choices=["minicpm-frames", "minicpm-interleave", "video_url"],
|
|
default="minicpm-frames",
|
|
help="Multimodal packing. "
|
|
"'minicpm-frames' (default): OmniEvalKit MiniCPM videomme recipe — sampled frames "
|
|
"as image_url only (max_frames=96). "
|
|
"'minicpm-interleave': OmniEvalKit videomme_short recipe — 1fps frame/audio pairs "
|
|
"(max_frames=64). "
|
|
"'video_url': single video_url part (models with native video input).",
|
|
)
|
|
group.add_argument(
|
|
"--videomme-max-frames",
|
|
type=int,
|
|
default=None,
|
|
help="Override max sampled frames (OmniEvalKit defaults: 96 for minicpm-frames, 64 for minicpm-interleave).",
|
|
)
|
|
group.add_argument(
|
|
"--videomme-duration",
|
|
type=str,
|
|
choices=["all", "short", "medium", "long"],
|
|
default="all",
|
|
help="Filter by Video-MME duration bucket (default: all).",
|
|
)
|
|
group.add_argument(
|
|
"--videomme-use-subtitle",
|
|
action="store_true",
|
|
default=False,
|
|
help="Prepend subtitle text to the user prompt (Video-MME w/ subs setting).",
|
|
)
|
|
group.add_argument(
|
|
"--videomme-inline-local-video",
|
|
action="store_true",
|
|
default=False,
|
|
help="Embed local frames/audio as base64 data URLs so the server does not need "
|
|
"--allowed-local-media-path. Increases request size; use for small --num-prompts.",
|
|
)
|
|
group.add_argument(
|
|
"--videomme-save-eval-items",
|
|
action="store_true",
|
|
default=False,
|
|
help="Include per-request Video-MME accuracy rows in the saved JSON under "
|
|
"videomme_eval_items. Or set env VIDEOMME_SAVE_EVAL_ITEMS=1.",
|
|
)
|
|
|
|
|
|
def add_seed_tts_cli_args(parser: argparse.ArgumentParser) -> None:
|
|
"""Add CLI arguments for Seed-TTS benchmarks."""
|
|
group = parser.add_argument_group("Seed-TTS Dataset Options")
|
|
group.add_argument(
|
|
"--seed-tts-locale",
|
|
type=str,
|
|
choices=["en", "zh"],
|
|
default="en",
|
|
help="Which Seed-TTS split to load: en/meta.lst or zh/meta.lst under the dataset root.",
|
|
)
|
|
group.add_argument(
|
|
"--seed-tts-turns-per-session",
|
|
type=int,
|
|
default=1,
|
|
help="Group this many Seed-TTS target texts into one benchmark request. "
|
|
"The first row's reference audio and transcript are reused for every turn. "
|
|
"A model-native duplex session takes its target text once, in the session "
|
|
"context, so each turn runs in its own session.",
|
|
)
|
|
group.add_argument(
|
|
"--seed-tts-root",
|
|
type=str,
|
|
default=None,
|
|
help="Override root directory that contains en/ and zh/ (meta.lst + prompt-wavs). "
|
|
"If set, --dataset-path can still name the HF repo for logging; this path is used for files.",
|
|
)
|
|
group.add_argument(
|
|
"--seed-tts-file-ref-audio",
|
|
action="store_true",
|
|
default=False,
|
|
help="Send ref_audio as file:// URIs (smaller HTTP bodies). Requires the API server "
|
|
"to be started with --allowed-local-media-path covering the Seed-TTS dataset root. "
|
|
"Default is inline data:audio/wav;base64 so Qwen3-TTS works without that flag.",
|
|
)
|
|
group.add_argument(
|
|
"--seed-tts-inline-ref-audio",
|
|
action="store_true",
|
|
default=False,
|
|
help=argparse.SUPPRESS,
|
|
)
|
|
group.add_argument(
|
|
"--seed-tts-system-prompt",
|
|
type=str,
|
|
default=None,
|
|
help="Override chat system message for --backend openai-chat-omni (Qwen3-Omni TTS). "
|
|
"Default follows official Qwen3-Omni identity + zero-shot voice-clone instructions.",
|
|
)
|
|
group.add_argument(
|
|
"--seed-tts-wer-eval",
|
|
action="store_true",
|
|
default=False,
|
|
help="Keep synthesized audio as 24 kHz mono PCM for WER (works with "
|
|
"--backend openai-audio-speech or openai-chat-omni). Scoring follows "
|
|
"zhaochenyang20/seed-tts-eval (Whisper-large-v3 / Paraformer-zh + jiwer). "
|
|
"Sets SEED_TTS_WER_EVAL=1. Install: pip install 'vllm-omni[dev]'. "
|
|
"Optional: SEED_TTS_EVAL_DEVICE, SEED_TTS_HF_WHISPER_MODEL.",
|
|
)
|
|
group.add_argument(
|
|
"--seed-tts-wer-save-items",
|
|
action="store_true",
|
|
default=False,
|
|
help="Include per-utterance ASR rows in the saved JSON under key seed_tts_wer_eval_items. "
|
|
"Or set SEED_TTS_WER_SAVE_ITEMS=1.",
|
|
)
|
|
|
|
|
|
_OMNI_BENCH_DATASET_CHOICES = (
|
|
"daily-omni",
|
|
"omniinteract",
|
|
"seed-tts",
|
|
"seed-tts-text",
|
|
"seed-tts-design",
|
|
"ttsd",
|
|
"sound-effect",
|
|
"videomme",
|
|
)
|
|
|
|
|
|
def extend_omni_choices(parser: argparse.ArgumentParser) -> None:
|
|
"""Extend upstream argument choices with Omni-specific values."""
|
|
parsers = [parser]
|
|
shadow = getattr(parser, "_shadow", None)
|
|
if shadow is not None:
|
|
parsers.append(shadow)
|
|
|
|
for current_parser in parsers:
|
|
for action in current_parser._actions:
|
|
if action.dest == "dataset_name" and action.choices is not None:
|
|
extra = [choice for choice in _OMNI_BENCH_DATASET_CHOICES if choice not in action.choices]
|
|
if extra:
|
|
action.choices = list(action.choices) + extra
|
|
if action.dest == "backend" and action.choices is not None:
|
|
extra = [choice for choice in ("openai-image-edits-omni",) if choice not in action.choices]
|
|
if extra:
|
|
action.choices = list(action.choices) + extra
|
|
|
|
|
|
def update_omni_help(parser: argparse.ArgumentParser) -> None:
|
|
"""Update upstream argument help text to describe Omni-specific behavior."""
|
|
for action in parser._actions:
|
|
if action.dest == "num_prompts":
|
|
action.help = (
|
|
f"{action.help} OmniInteract uses {_DEFAULT_OMNIINTERACT_NUM_PROMPTS} when this option is omitted; "
|
|
"0 selects all available cases."
|
|
)
|
|
if action.dest == "percentile_metrics":
|
|
action.help = (
|
|
"Comma-separated list of selected metrics to report percentiles. "
|
|
'For text metrics, "ttft", "tpot", and "itl" affect the global benchmark and text '
|
|
'stage metrics. "tpop" also requests text TPOT/TPOP globally and per stage, and internal '
|
|
'stream TPOP. "ttfc", "tpoc", and "icl" only affect internal stream stage metrics. '
|
|
'Audio metrics include "audio_ttfp", "audio_rtf", "audio_duration", and "audio_underrun".'
|
|
)
|
|
if action.dest == "random_mm_limit_mm_per_prompt":
|
|
action.help = (
|
|
"Per-modality hard caps for items attached per request, e.g. "
|
|
'\'{"image": 3, "video": 0, "audio": 1}\'. The sampled per-request item '
|
|
"count is clamped to the sum of these limits. When a modality "
|
|
"reaches its cap, its buckets are excluded and probabilities are "
|
|
"renormalized."
|
|
)
|
|
if action.dest == "random_mm_bucket_config":
|
|
action.help = (
|
|
"The bucket config is a dictionary mapping a multimodal item"
|
|
"sampling configuration to a probability."
|
|
"Currently allows for 3 modalities: audio, images and videos. "
|
|
"A bucket key is a tuple of (height, width, num_frames)"
|
|
"The value is the probability of sampling that specific item. "
|
|
"Example: "
|
|
"--random-mm-bucket-config "
|
|
"{(256, 256, 1): 0.5, (720, 1280, 16): 0.4, (0, 1, 5): 0.10} "
|
|
"First item: images with resolution 256x256 w.p. 0.5"
|
|
"Second item: videos with resolution 720x1280 and 16 frames "
|
|
"Third item: audios with 1s duration and 5 channels w.p. 0.1"
|
|
"OBS.: If the probabilities do not sum to 1, they are normalized."
|
|
)
|
|
|
|
|
|
def add_omni_args(parser: argparse.ArgumentParser) -> None:
|
|
"""Register all vLLM-Omni serving benchmark arguments."""
|
|
add_daily_omni_cli_args(parser)
|
|
add_videomme_cli_args(parser)
|
|
add_omniinteract_cli_args(parser)
|
|
add_seed_tts_cli_args(parser)
|
|
add_multi_stage_cli_args(parser)
|
|
add_diffusion_cli_args(parser)
|
|
|
|
|
|
def preprocess_serve_args(args: argparse.Namespace) -> None:
|
|
"""Apply serving benchmark CLI transformations after parsing."""
|
|
if getattr(args, "dataset_name", None) == "omniinteract":
|
|
if getattr(args, "backend", None) != "openai-realtime-duplex":
|
|
raise ValueError("OmniInteract requires --backend openai-realtime-duplex")
|
|
if getattr(args, "endpoint", None) != "/v1/realtime":
|
|
raise ValueError("OmniInteract requires --endpoint /v1/realtime")
|
|
if not getattr(args, "omniinteract_ref_audio", None):
|
|
raise ValueError("OmniInteract requires --omniinteract-ref-audio")
|
|
if getattr(args, "omniinteract_evaluate", False) and not getattr(args, "omniinteract_judge_model", None):
|
|
raise ValueError("OmniInteract evaluation requires --omniinteract-judge-model")
|
|
if getattr(args, "omniinteract_video_list", None):
|
|
explicit = getattr(args, "explicit_keys", ())
|
|
if "seed" in explicit:
|
|
raise ValueError("--omniinteract-video-list cannot be combined with --seed")
|
|
if "dataset_path" in explicit:
|
|
raise ValueError("--omniinteract-video-list cannot be combined with --dataset-path")
|
|
if getattr(args, "omniinteract_scenario_tags", None):
|
|
raise ValueError("--omniinteract-video-list cannot be combined with --omniinteract-scenario-tags")
|
|
if getattr(args, "omniinteract_scenario_focus", False):
|
|
raise ValueError("--omniinteract-video-list cannot be combined with --omniinteract-scenario-focus")
|
|
if getattr(args, "omniinteract_scenario_focus", False) and not getattr(
|
|
args, "omniinteract_scenario_tags", None
|
|
):
|
|
raise ValueError("--omniinteract-scenario-focus requires --omniinteract-scenario-tags")
|
|
if getattr(args, "ignore_eos", False):
|
|
raise ValueError("OmniInteract does not support --ignore-eos")
|
|
if getattr(args, "profile", False):
|
|
raise ValueError("OmniInteract does not support --profile")
|
|
if getattr(args, "skip_tokenizer_init", False):
|
|
raise ValueError("OmniInteract does not support --skip-tokenizer-init")
|
|
if float(getattr(args, "probe_request_rate", 0.0) or 0.0) > 0:
|
|
raise ValueError("OmniInteract does not support --probe-request-rate")
|
|
if "num_prompts" not in getattr(args, "explicit_keys", ()):
|
|
args.num_prompts = _DEFAULT_OMNIINTERACT_NUM_PROMPTS
|
|
max_concurrency = getattr(args, "max_concurrency", None)
|
|
if max_concurrency is None:
|
|
args.max_concurrency = 1
|
|
elif max_concurrency <= 0:
|
|
raise ValueError("OmniInteract requires --max-concurrency to be positive")
|
|
extra_body = dict(getattr(args, "extra_body", None) or {})
|
|
bot_task = getattr(args, "bot_task", None)
|
|
backend = getattr(args, "backend", None)
|
|
endpoint = getattr(args, "endpoint", None)
|
|
# serve.py remaps implicit backend to the endpoint path for image edits;
|
|
# inject bot_task for both the named backend and /v1/images/edits.
|
|
if bot_task is not None and (
|
|
backend in ("openai-image-edits-omni", "/v1/images/edits") or endpoint == "/v1/images/edits"
|
|
):
|
|
extra_body.setdefault("bot_task", bot_task)
|
|
args.extra_body = extra_body
|