Stream Typed Events¶
A stream yields typed events while the request runs: text deltas, reasoning, timing marks, and attempt failures. This page shows the patterns a terminal frontend needs; the full event vocabulary is in the API reference.
import anyinfer as ai
client = ai.Client([ai.ProviderSettings.of("ollama")])
with client.stream("Explain TCP slow start.", target="ollama:qwen3:8b") as stream:
for event in stream:
if isinstance(event, ai.TextDelta):
print(event.text, end="", flush=True)
result = stream.result
print(
f"\n\n{result.usage.output_tokens} tokens, "
f"first token in {result.timing.first_token_ms:.0f} ms"
)
import anyinfer as ai
async with ai.AsyncClient([ai.ProviderSettings.of("ollama")]) as client:
async with client.stream("Explain TCP slow start.", target="ollama:qwen3:8b") as stream:
async for event in stream:
if isinstance(event, ai.TextDelta):
print(event.text, end="", flush=True)
result = stream.result
print(
f"\n\n{result.usage.output_tokens} tokens, "
f"first token in {result.timing.first_token_ms:.0f} ms"
)
Use the Context Manager¶
Leaving the block early cancels the in-flight request. Without it, an abandoned stream keeps generating — and, on a hosted provider, keeps billing:
with client.stream(prompt, target=target) as stream:
for event in stream:
if isinstance(event, ai.TextDelta):
print(event.text, end="", flush=True)
if user_pressed_escape():
break # the request is cancelled on the way out
Show Thinking Separately¶
Reasoning models emit a separate channel, excluded from the answer text:
for event in stream:
match event:
case ai.ReasoningDelta(text=t):
print(dim(t), end="", flush=True)
case ai.TextDelta(text=t):
print(t, end="", flush=True)
Measure Time to First Token¶
for event in stream:
if isinstance(event, ai.TimingMark) and event.name == "first_token":
print(f"[{event.at_ms:.0f} ms] ", end="", flush=True)
TTFT is measured by the core against time.monotonic(), identically for every provider, so
numbers from different backends are directly comparable. To export timings to a metrics
system rather than print them, see observability.
Show Fallback as It Happens¶
When a fallback chain is in play, AttemptFailed events allow you to show
the switch as it happens rather than leaving the user staring at a stalled cursor:
for event in stream:
match event:
case ai.AttemptFailed(record=record):
print(f"[{record.target} failed: {record.error.type_name}]")
case ai.TextDelta(text=t):
print(t, end="", flush=True)
Key Takeaways
- Use the context-manager form; leaving the block early cancels the in-flight request instead of letting a hosted provider keep generating and billing.
- Reasoning text arrives as
ReasoningDelta, a channel separate from the answer, so you can dim it or drop it without parsing anything. - TTFT is measured by the core, not the provider, so numbers from different backends are directly comparable.
AttemptFailedevents surface retries and fallback while they happen, not after.