Skip to main content
The gateway relays ten structured progress events to WebSocket clients once they negotiate the streaming capability, so UIs can paint reasoning, tool progress, model fallbacks, retries, live todos, tool results, and the final answer as they arrive.
The agent emits token, reasoning, and tool events; the gateway forwards each to the WS client, which distinguishes a provisional accepted ack from the final answer.

Quick Start

1

Open a streaming client

2

Print each event type


Event Vocabulary

Each streaming event maps to an EventType in praisonaiagents.gateway.protocols and is advertised in hello_ok.features["events"] when the client negotiates streaming.

What each new event means to a live UI

  • model_fallback_stream — the primary model went down and the agent switched to a backup mid-turn. Render a “switched to backup model” banner instead of leaving the token stream frozen. metadata.from_model is the model that failed, metadata.to_model is now serving the turn, metadata.reason_category classifies the failure (rate_limit, unavailable, error), and metadata.fallback_index is the 0-based position in the fallback chain.
  • retry_stream — the agent is backing off before retrying a rate-limited or transient failure. Render a countdown (“retrying in 3s… attempt 2/5”) so the pause reads as progress, not a freeze. metadata.attempt / metadata.max_attempts drive the counter, metadata.delay is seconds until the next attempt, and metadata.reason is a short human string.
  • todo_stream — the agent mutated its plan. metadata.todos is the full ordered list (each item carries its status and text), so re-render the whole checklist from it.
  • tool_result_stream — a tool finished with a result. tool_call carries the name, args, id, and result — render a tool result card; metadata holds any extra context.
reasoning_stream, tool_progress_stream, stream_error, and the four newer events — model_fallback_stream, retry_stream, todo_stream, and tool_result_stream — are additive. Existing token_stream consumers keep working unchanged — gate each new event on client.supports_event(...) so a legacy gateway that never advertises them degrades silently.
reasoning_stream now fires for async agent runs on OpenAI Responses-API reasoning models too, matching the sync path. Gateway consumers of async runs on these models will start seeing this signal where they previously did not.

Response Frame States

Every WebSocket response frame carries a status: a provisional accepted ack arrives first, then the real answer as final with a structured outcome.
Never treat type: "response" alone as “done”. Check status: "final" and read outcome.status before clearing the spinner.

User Interaction Flow

A streaming UI paints each event as it arrives — reasoning bubble, then a tool progress chip, then the final answer. When the primary model fails mid-turn, model_fallback_stream and retry_stream fill the visible-progress gap so the token stream never looks frozen.

Common Patterns


Best Practices

Wrap new-event rendering in client.supports_event("reasoning_stream") and client.supports_event("tool_progress_stream") so legacy gateways — which never advertise these events — degrade silently instead of breaking.
A stream_error frame does not end the run. The gateway may still deliver a final response with outcome.status="error". Surface the error but keep listening until stream_end / final.
Both the provisional ack and the real answer share type: "response". Branch on status: show a spinner on accepted, render only on final, and read outcome.status for the completion state.

Gateway Handshake Protocol

Negotiate streaming and read the advertised event set

Gateway Client

Reconnecting client that streams these events

Frame Codec

Validate inbound frames at the WebSocket boundary

Session Protocol

How sessions carry these events