Overview
Streaming lets your application receive execution updates while work is still running. Instead of waiting for the final result, you can render LLM tokens, tool activity, Flow lifecycle events, and conversation messages as they happen. CrewAI has two streaming surfaces:| Surface | Used by | Output |
|---|---|---|
| Frame streaming | Flows, direct LLM calls, conversational turns | Ordered StreamFrame objects |
| Crew chunk streaming | Crews with stream=True | CrewStreamingOutput chunks |
StreamFrame
AStreamFrame is the common object emitted by streamable runtimes:
| Field | Use it for |
|---|---|
channel | Routing frames to the right UI region |
type | Handling a specific event inside a channel |
content | Printing token-like text |
event | Reading structured metadata, such as tool names or message roles |
seq | Preserving execution order |
Channels
Frames are grouped into high-level channels:| Channel | Contains |
|---|---|
llm | LLM call lifecycle, text chunks, and thinking chunks |
flow | Flow lifecycle, method execution, routing, pause, and resume events |
tools | Tool usage start, finish, and error events |
messages | Conversation transcript events |
lifecycle | Runtime lifecycle events that do not belong to another channel |
custom | Events that do not map to a built-in channel |
Stream Sessions
Frame streaming returns a stream session:stream.result. Reading the result too early raises an error because the runtime may still be producing frames.
Channel Projections
Use channel projections when you only need one kind of frame:| Projection | Frames |
|---|---|
stream.events | All frames |
stream.llm | LLM frames |
stream.flow | Flow frames |
stream.tools | Tool frames |
stream.messages | Conversation message frames |
stream.interleave([...]) | Selected channels in relative order |
Entrypoints
Use the entrypoint that matches the runtime you are streaming:| Runtime | Streaming entrypoint |
|---|---|
| Flow | flow.stream_events(...) |
Flow with stream=True | flow.kickoff(...) returns a stream session |
| Async Flow | flow.astream(...) or await flow.kickoff_async(...) when stream=True |
| Direct LLM call | llm.stream_events(...) |
| Conversational Flow turn | flow.stream_turn(...) |
| Crew | Crew(..., stream=True).kickoff(...) returns CrewStreamingOutput |
llm.call(...) still returns the final assembled LLM result. Use llm.stream_events(...) when you want to iterate over LLM chunks as they arrive.
