Quick Start
1
Simplest usage
Iterate the projected state and render it — the reducer folds every event for you.
2
With a resume snapshot
Seed the projection with a snapshot on reconnect so the view converges instead of drifting.
3
Bound retention on a long-lived session
Cap how many finished runs stay tracked; active streams are never evicted.
4
Pure-core usage without the wrapper
Drive the reducer directly for third-party clients or tests — no transport required.
How It Works
The reducer folds a snapshot plus live events into an immutable state, reconciling every duplicate along the way.De-duplication Behaviours
Three distinct reconciliations keep the view correct, each keyed on a different identifier.- Identity de-dup (message_id)
- Optimistic reconciliation (request_id)
- Snapshot convergence after a gap
A final assistant message that arrives both as a
STREAM_END and as a persisted transcript row renders once, keyed by message_id.Configuration Options
The reducer constructor takes a single option.SessionProjectionState — the immutable value returned by every apply call:
RunView — the immutable per-run streaming view:
GatewayClient.project()
Yields a
SessionProjectionState after the initial snapshot (if any) and after every subsequent event.
TypeScript Parity
Thepraisonai-ts SDK ships the same reducer, importing from the package root. It accepts both camelCase (SDK) and snake_case (wire) keys — messageId/message_id, requestId/request_id, senderId/sender_id, runId/run_id — so wire frames from the Python gateway drop straight in with identical de-dup, reconciliation, gap and retention semantics.
- Simplest usage
- With snapshot
Common Patterns
- Render a dashboard from a live session
- Resume after reconnect
- Third-party client without the wrapper
Choose Your Entry Point
Pick the wrapper when you use the bundled client; drive the pure core everywhere else.Best Practices
Feed a resume snapshot on reconnect
Feed a resume snapshot on reconnect
Pass a snapshot so the view converges instead of drifting —
apply_snapshot() resets gap tracking and rebuilds the transcript from persisted rows.Watch has_transport_gap in your UI
Watch has_transport_gap in your UI
Pair
state.has_transport_gap with client.resync() on the transport side to heal a sequence hole after a reconnect.Tune max_tracked_runs for long-lived sessions
Tune max_tracked_runs for long-lived sessions
Lower
max_tracked_runs for dashboards that run for days; leave the default 200 for a chat UI.Treat state as read-only
Treat state as read-only
Never mutate
state.entries or state.runs — they are immutable snapshots. A previous state is never mutated in place by a later apply().Prefer project() over hand-rolled event handling
Prefer project() over hand-rolled event handling
client.project() is the SDK primitive that removes double-rendered answers, reconnect flicker and unbounded memory growth.Related
Gateway Client
Reconnecting transport and raw event stream
Stream Events
Live progress events forwarded over WebSocket
Frame Codec
The
request_id reconciliation keySession Persistence
Where durable transcript rows come from

