[bench] Agentic support for bench_serving.py (#25016)
This commit is contained in:
@@ -1136,6 +1136,24 @@ def calculate_metrics(
|
|||||||
MULTI_TURN_BACKENDS = {"sglang-oai-chat", "vllm-chat", "lmdeploy-chat"}
|
MULTI_TURN_BACKENDS = {"sglang-oai-chat", "vllm-chat", "lmdeploy-chat"}
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_round_messages(turn: Any) -> Optional[List[Dict[str, str]]]:
|
||||||
|
"""Normalize a multi-turn round to a list of message dicts.
|
||||||
|
|
||||||
|
Accepts ``str`` (single user message) or ``List[Dict]`` with role/content
|
||||||
|
(e.g. multiple tool observations bundled into one round). Returns ``None``
|
||||||
|
on any other shape so callers can also use it as a predicate.
|
||||||
|
"""
|
||||||
|
if isinstance(turn, str):
|
||||||
|
return [{"role": "user", "content": turn}]
|
||||||
|
if (
|
||||||
|
isinstance(turn, list)
|
||||||
|
and turn
|
||||||
|
and all(isinstance(m, dict) and "role" in m and "content" in m for m in turn)
|
||||||
|
):
|
||||||
|
return [{"role": m["role"], "content": m["content"]} for m in turn]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def wrap_multi_turn_request_func(request_func: Callable, backend: str) -> Callable:
|
def wrap_multi_turn_request_func(request_func: Callable, backend: str) -> Callable:
|
||||||
assert (
|
assert (
|
||||||
backend in MULTI_TURN_BACKENDS
|
backend in MULTI_TURN_BACKENDS
|
||||||
@@ -1145,12 +1163,19 @@ def wrap_multi_turn_request_func(request_func: Callable, backend: str) -> Callab
|
|||||||
request_func_input: RequestFuncInput,
|
request_func_input: RequestFuncInput,
|
||||||
pbar: Optional[tqdm] = None,
|
pbar: Optional[tqdm] = None,
|
||||||
) -> List[RequestFuncOutput]:
|
) -> List[RequestFuncOutput]:
|
||||||
prompts: List[str] = request_func_input.prompt
|
prompts = request_func_input.prompt
|
||||||
prev_messages: List[Dict[str, str]] = []
|
prev_messages: List[Dict[str, str]] = []
|
||||||
outputs = []
|
outputs = []
|
||||||
|
|
||||||
for round_index in range(len(prompts)):
|
for round_index in range(len(prompts)):
|
||||||
prev_messages.append({"role": "user", "content": prompts[round_index]})
|
normalized = _normalize_round_messages(prompts[round_index])
|
||||||
|
if normalized is None:
|
||||||
|
raise ValueError(
|
||||||
|
f"Multi-turn round {round_index} must be a str or a "
|
||||||
|
"non-empty List[Dict] of role/content messages, got: "
|
||||||
|
f"{type(prompts[round_index]).__name__}"
|
||||||
|
)
|
||||||
|
prev_messages.extend(normalized)
|
||||||
|
|
||||||
inner_input = replace(
|
inner_input = replace(
|
||||||
copy.deepcopy(request_func_input), prompt=copy.deepcopy(prev_messages)
|
copy.deepcopy(request_func_input), prompt=copy.deepcopy(prev_messages)
|
||||||
@@ -1198,14 +1223,13 @@ async def benchmark(
|
|||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown backend: {backend}")
|
raise ValueError(f"Unknown backend: {backend}")
|
||||||
|
|
||||||
# Check for multi-turn: prompt is a list of strings (not OpenAI messages dicts)
|
# Multi-turn iff prompt[0] is a valid per-round payload. Single-shot
|
||||||
# Multi-turn format: ["turn1", "turn2", ...] - list of strings
|
# OpenAI messages (List[Dict]) is excluded since its first element is a dict.
|
||||||
# OpenAI format: [{"role": "user", "content": "..."}, ...] - list of dicts
|
|
||||||
first_prompt = input_requests[0].prompt
|
first_prompt = input_requests[0].prompt
|
||||||
is_multi_turn = (
|
is_multi_turn = (
|
||||||
isinstance(first_prompt, list)
|
isinstance(first_prompt, list)
|
||||||
and len(first_prompt) > 0
|
and bool(first_prompt)
|
||||||
and isinstance(first_prompt[0], str)
|
and _normalize_round_messages(first_prompt[0]) is not None
|
||||||
)
|
)
|
||||||
if is_multi_turn:
|
if is_multi_turn:
|
||||||
request_func = wrap_multi_turn_request_func(request_func, backend=backend)
|
request_func = wrap_multi_turn_request_func(request_func, backend=backend)
|
||||||
|
|||||||
@@ -106,6 +106,20 @@ def _normalize_prompt(row: Dict[str, Any]) -> Tuple[Any, str]:
|
|||||||
and all(isinstance(item, str) for item in prompt)
|
and all(isinstance(item, str) for item in prompt)
|
||||||
):
|
):
|
||||||
return prompt, "multi_turn"
|
return prompt, "multi_turn"
|
||||||
|
if (
|
||||||
|
isinstance(prompt, list)
|
||||||
|
and prompt
|
||||||
|
and all(
|
||||||
|
isinstance(item, list)
|
||||||
|
and item
|
||||||
|
and all(
|
||||||
|
isinstance(m, dict) and "role" in m and "content" in m for m in item
|
||||||
|
)
|
||||||
|
for item in prompt
|
||||||
|
)
|
||||||
|
):
|
||||||
|
# Multi-turn with N messages per round (e.g. tool observations).
|
||||||
|
return prompt, "multi_turn"
|
||||||
if (
|
if (
|
||||||
isinstance(prompt, list)
|
isinstance(prompt, list)
|
||||||
and prompt
|
and prompt
|
||||||
|
|||||||
Reference in New Issue
Block a user