bench: run one server at a time, match thinking mode, fix tools package

Refinements from end-to-end bring-up on the GPU host:

- Run each system start→suites→stop in sequence. Two BF16 8B models don't
  co-reside on one 32GB GPU, and a resident idle engine would distort the
  other's latency/throughput.
- Match generation mode: xserv hardcodes Qwen3 thinking off, so send
  chat_template_kwargs={enable_thinking:false} to llama.cpp via a per-endpoint
  extra_body. --enable-thinking opts back into thinking mode.
- Add tools/__init__.py so `python3 -m tools.bench.runner` resolves our package
  instead of a site-packages `tools` (nvfuser ships one that shadowed it).
- Document offline-GPU-host workflow, thinking-match, and the xserv 8192 OOM
  finding that the bench surfaced.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 11:40:07 +08:00
parent 49c7653222
commit 7cb9ee3870
7 changed files with 102 additions and 28 deletions

View File

@@ -55,6 +55,7 @@ async def chat_stream(
temperature: float = 0.0,
api_key: str | None = None,
timeout: float = 1800.0,
extra_body: dict | None = None,
) -> StreamResult:
payload: dict[str, Any] = {
"model": model,
@@ -66,6 +67,8 @@ async def chat_stream(
# llama-server returns usage in the final stream chunk when this is set;
# xserv ignores unknown fields, so this is harmless there.
payload["stream_options"] = {"include_usage": True}
if extra_body:
payload.update(extra_body)
headers = {"Content-Type": "application/json"}
if api_key:
@@ -135,6 +138,7 @@ async def chat_concurrent(
api_key: str | None = None,
timeout: float = 1800.0,
concurrency: int,
extra_body: dict | None = None,
) -> tuple[list[StreamResult], float]:
"""Fire `concurrency` requests in parallel waves. Returns per-request results
plus wall-clock elapsed time of the entire batch."""
@@ -146,7 +150,7 @@ async def chat_concurrent(
return await chat_stream(
client, base_url, model, messages,
max_tokens=max_tokens, temperature=temperature,
api_key=api_key, timeout=timeout,
api_key=api_key, timeout=timeout, extra_body=extra_body,
)
t0 = time.perf_counter()
results = await asyncio.gather(*(one(p) for p in prompts))