Step 1: Stream and time it
Pennant Outdoor's support assistant answers in about 120 words. Without streaming, the customer stares at a spinner until the last word is generated. With streaming, words appear as they are produced, so the wait that matters is time to first token (TTFT).
llmsim.py is a small model server that behaves like a hosted provider: it streams Server-Sent Events, its
TTFT grows with the prompt it has to read, and it caches prompt prefixes. The Run button starts it on port 8090.
Its answers are filler text; what matters here is how they arrive.
Do this
1. Write stream_chat(client, messages, max_tokens): call client.chat.completions.create(...) with
stream=True and stream_options={"include_usage": True}, loop over the chunks, and return
{"text", "ttft", "total", "chunks", "usage"}: the joined text, seconds to the first non-empty
delta.content, seconds to the end, the number of text pieces, and the final chunk.usage.model_dump().
2. Write is_buffered(result): True when the first text arrived at 80 % or more of the total time.
3. Run. It streams from the simulator and from the lab's real model gateway. Compare the two lines.