Skip to content

Commit 89b32ea

Browse files
author
Philipp Wacker
committed
examples/vllm-bench: add long-text scenario (8 paragraphs, 1500 tokens)
The long-text scenario shows the buffering vs streaming difference most dramatically: with the buffer-all path, the client receives nothing for 20+ seconds and then the entire 1500-token response at once. With native streaming, the first token arrives in tens of milliseconds and the response flows progressively.
1 parent a96a3d9 commit 89b32ea

2 files changed

Lines changed: 33 additions & 15 deletions

File tree

examples/vllm-bench/README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,36 @@ non-streaming — the client received nothing until the model finished.
1515
With native parser-side streaming (`parser.extract_tool_calls_streaming`,
1616
implemented by every concrete vLLM 0.23+ tool parser), each delta can be
1717
classified per-token: emit as content, emit as a structured tool_call, or
18-
suppress. This benchmark shows the difference.
18+
suppress.
1919

20-
## Two scenarios
20+
## Three scenarios
2121

2222
| Scenario | Request | Expected outcome |
2323
|---|---|---|
24-
| `tool_call` | "What is the weather in Paris? Please use the tool." | Model calls `get_weather`. `delta.tool_calls` chunks; no content leak. |
25-
| `plain_text` | "Explain in 3 short sentences what a hash table is. Do NOT call any tool." | Model writes prose. With the streaming parser, content streams progressively; without it, the entire response arrives in one chunk. |
24+
| `tool_call` | "What is the weather in Paris? Please use the tool." | Model calls `get_weather`. `delta.tool_calls` chunks; no content leak. |
25+
| `plain_text_short` | "Explain in 3 short sentences what a hash table is. Do NOT call any tool." | Model writes ~3 sentences. |
26+
| `plain_text_long` | "Write a thorough 8-paragraph explanation of how Python's GIL works…" | Model writes ~1500 tokens of prose. |
27+
28+
The **long scenario** is where the streaming/buffering difference is most
29+
dramatic: with the buffer-all path, the client sees nothing for 20+ seconds
30+
and then everything at once; with native streaming, the first token arrives
31+
in <100ms and the response flows progressively.
2632

2733
## What the script reports
2834

2935
For each scenario, across N runs:
3036

3137
- `ttf_content_s` — time until the first `delta.content` chunk
3238
- `ttf_tool_s` — time until the first `delta.tool_calls` chunk
33-
- `n_content_chunks` — total number of distinct content deltas (1 = bundled, >1 = streamed)
39+
- `n_content_chunks` — total content deltas (1 = bundled, >>1 = streamed)
3440
- `n_tool_chunks` — total tool_call deltas
3541
- `total_s` — total wall-clock until `[DONE]`
3642
- `finish_reason``tool_calls` / `stop` / `length`
3743

44+
The big tell is **`n_content_chunks` vs `total_s` ratio**:
45+
- Buffer-all: `n_content_chunks` ≈ 1, `ttf_content_s``total_s` (one chunk at end)
46+
- Streaming: `n_content_chunks` ≈ token count, `ttf_content_s` ≈ first-token latency
47+
3848
## Usage
3949

4050
```bash

examples/vllm-bench/ttft_streaming_tool_parser.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
"""
33
TTFT benchmark for the vLLM backend's streaming + tool-parser path.
44
5-
Measures time-to-first-token (TTFT) for two scenarios against a running
6-
LocalAI instance with a vLLM-backed chat model:
5+
Three scenarios:
6+
1. tool_call — request mentions a tool; model is expected to call it
7+
2. plain_text_short — request offers a tool but explicitly asks for ~3 sentences
8+
3. plain_text_long — same as above but asks for ~8 paragraphs (1500 tokens)
79
8-
1. tool_call — request mentions a tool; model is expected to call it
9-
2. plain_text — request offers a tool but explicitly asks for prose
10-
11-
Useful to validate the difference between:
12-
- the buffer-all path (#10346): plain_text TTFT ≈ total response time
13-
- the native-streaming path (this PR): plain_text TTFT ≈ true first-token time
10+
The long scenario shows the dramatic difference between buffering and
11+
streaming most clearly: with buffer-all, the client sees nothing for
12+
20+ seconds; with native streaming, the first token arrives in <100 ms.
1413
1514
Usage:
1615
python ttft_streaming_tool_parser.py \\
@@ -45,12 +44,21 @@
4544
"max_tokens": 80,
4645
},
4746
{
48-
"label": "plain_text",
47+
"label": "plain_text_short",
4948
"messages": [{"role": "user",
5049
"content": "Explain in 3 short sentences what a hash table is. "
5150
"Do NOT call any tool."}],
5251
"max_tokens": 200,
5352
},
53+
{
54+
"label": "plain_text_long",
55+
"messages": [{"role": "user",
56+
"content": "Write a thorough 8-paragraph explanation of how "
57+
"Python's GIL works, including history, current "
58+
"state, no-GIL build, and alternatives. Be "
59+
"detailed. Do NOT call any tool."}],
60+
"max_tokens": 1500,
61+
},
5462
]
5563

5664

@@ -125,7 +133,7 @@ def main():
125133
help="LocalAI base URL (default: %(default)s)")
126134
p.add_argument("--model", default="coder", help="Model name (default: %(default)s)")
127135
p.add_argument("--runs", type=int, default=3, help="Repetitions per scenario (default: %(default)s)")
128-
p.add_argument("--timeout", type=int, default=120, help="Per-request timeout in seconds")
136+
p.add_argument("--timeout", type=int, default=180, help="Per-request timeout in seconds")
129137
p.add_argument("--label", default="run",
130138
help="Tag for the JSON output file (default: %(default)s)")
131139
args = p.parse_args()

0 commit comments

Comments
 (0)