Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ async function expressExample() {
})

for await (const event of agent.stream(prompt)) {
// Events automatically serialize to compact JSON via toJSON().
// Only relevant data fields are included — the full Agent instance,
// Tool classes, and mutable hook flags (cancel/retry) are excluded.
res.write(`${JSON.stringify(event)}\n`)
}
res.end()
Expand All @@ -51,4 +54,4 @@ async function expressExample() {
app.post('/stream', handleStreamRequest)
app.listen(3000)
// --8<-- [end:express_example]
}
}
52 changes: 51 additions & 1 deletion src/content/docs/user-guide/concepts/streaming/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,56 @@ See [Graph streaming](../multi-agent/graph.md#streaming-events) and [Swarm strea
</Tab>
</Tabs>

### Event Serialization

<Tabs>
<Tab label="Python">

Python streaming events are plain dictionaries. The SDK does not include a built-in serialization filter — you have full control over which events and fields to forward from your processes and servers.

When serving streamed responses (for example, over SSE or WebSockets), you can filter the yielded events to keep payloads compact:

```python
import json

def filter_event(event: dict) -> dict | None:
"""Filter streaming events to only forward relevant data over the wire."""
# Forward text deltas for real-time display
if "data" in event:
return {"type": "text", "data": event["data"]}

# Forward tool usage for progress indicators
if "current_tool_use" in event and event["current_tool_use"].get("name"):
return {"type": "tool", "name": event["current_tool_use"]["name"]}

# Forward the final result
if "result" in event:
return {"type": "result", "stop_reason": str(event["result"].stop_reason)}

# Skip everything else (lifecycle signals, raw deltas, reasoning, etc.)
return None


async for event in agent.stream_async("Hello"):
filtered = filter_event(event)
if filtered:
await response.write(f"data: {json.dumps(filtered)}\n\n")
```

This approach lets you tailor the streamed output to your use case — for example, forwarding only text deltas for a chat UI or including tool events for a progress dashboard.
</Tab>
<Tab label="TypeScript">

Every event class implements a `toJSON()` method that `JSON.stringify()` calls automatically. Each serialized event retains its `type` discriminator and the relevant data fields — matching the general shape of the class — while excluding in-memory runtime references (`agent`, `orchestrator`, `state`, `tool`) and mutable hook properties (`cancel`, `retry`). `Error` objects are converted to `{ message: string }`. This applies to single-agent, multi-agent, and A2A events alike.

You can filter which events to forward to the client:

```typescript
--8<-- "user-guide/concepts/streaming/overview.ts:event_serialization"
```
</Tab>
</Tabs>

## Quick Examples
<Tabs>
<Tab label="Python">
Expand Down Expand Up @@ -346,4 +396,4 @@ orchestrator_callback("What is 3+3?")

- Learn about [Async Iterators](async-iterators.md) for asynchronous streaming
- Explore [Callback Handlers](callback-handlers.md) for synchronous event processing
- See the [Agent API Reference](@api/python/strands.agent.agent) for complete method documentation
- See the [Agent API Reference](@api/python/strands.agent.agent) for complete method documentation
Comment thread
mkmeral marked this conversation as resolved.
31 changes: 31 additions & 0 deletions src/content/docs/user-guide/concepts/streaming/overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,34 @@ async function subAgentStreamingExample() {

// --8<-- [end:sub_agent_basic]
}

// Event Serialization Example
async function eventSerializationExample() {
const agent = new Agent()

// --8<-- [start:event_serialization]
for await (const event of agent.stream('Hello')) {
switch (event.type) {
// Forward text deltas for real-time display
case 'modelStreamUpdateEvent':
if (
event.event.type === 'modelContentBlockDeltaEvent' &&
event.event.delta.type === 'textDelta'
) {
console.log(`data: ${JSON.stringify({ type: 'text', text: event.event.delta.text })}`)
}
break

// Forward tool names for progress indicators
case 'beforeToolCallEvent':
console.log(`data: ${JSON.stringify({ type: 'tool', name: event.toolUse.name })}`)
break

// Forward the final result
case 'agentResultEvent':
console.log(`data: ${JSON.stringify(event)}`)
break
}
}
// --8<-- [end:event_serialization]
}
Loading