mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-23 07:45:10 +00:00
Unify flow streaming frame items
This commit is contained in:
@@ -52,7 +52,7 @@ class ResearchFlow(Flow):
|
||||
|
||||
## Synchronous Streaming
|
||||
|
||||
When you call `kickoff()` on a flow with streaming enabled, it returns a `FlowStreamingOutput` object that you can iterate over:
|
||||
When you call `kickoff()` on a flow with streaming enabled, it returns a stream session that yields ordered `StreamFrame` items:
|
||||
|
||||
```python Code
|
||||
flow = ResearchFlow()
|
||||
@@ -60,44 +60,43 @@ flow = ResearchFlow()
|
||||
# Start streaming execution
|
||||
streaming = flow.kickoff()
|
||||
|
||||
# Iterate over chunks as they arrive
|
||||
for chunk in streaming:
|
||||
print(chunk.content, end="", flush=True)
|
||||
# Iterate over stream items as they arrive
|
||||
for item in streaming:
|
||||
print(item.content, end="", flush=True)
|
||||
|
||||
# Access the final result after streaming completes
|
||||
result = streaming.result
|
||||
print(f"\n\nFinal output: {result}")
|
||||
```
|
||||
|
||||
### Stream Chunk Information
|
||||
### Stream Item Information
|
||||
|
||||
Each chunk provides context about where it originated in the flow:
|
||||
Each item provides both printable content and structured event data:
|
||||
|
||||
```python Code
|
||||
streaming = flow.kickoff()
|
||||
|
||||
for chunk in streaming:
|
||||
print(f"Agent: {chunk.agent_role}")
|
||||
print(f"Task: {chunk.task_name}")
|
||||
print(f"Content: {chunk.content}")
|
||||
print(f"Type: {chunk.chunk_type}") # TEXT or TOOL_CALL
|
||||
for item in streaming:
|
||||
print(f"Channel: {item.channel}")
|
||||
print(f"Type: {item.type}")
|
||||
print(f"Content: {item.content}")
|
||||
print(f"Event payload: {item.event}")
|
||||
```
|
||||
|
||||
### Accessing Streaming Properties
|
||||
|
||||
The `FlowStreamingOutput` object provides useful properties and methods:
|
||||
The stream session provides useful properties and methods:
|
||||
|
||||
```python Code
|
||||
streaming = flow.kickoff()
|
||||
|
||||
# Iterate and collect chunks
|
||||
for chunk in streaming:
|
||||
print(chunk.content, end="", flush=True)
|
||||
# Iterate and collect items
|
||||
for item in streaming:
|
||||
print(item.content, end="", flush=True)
|
||||
|
||||
# After iteration completes
|
||||
print(f"\nCompleted: {streaming.is_completed}")
|
||||
print(f"Full text: {streaming.get_full_text()}")
|
||||
print(f"Total chunks: {len(streaming.chunks)}")
|
||||
print(f"Total frames: {len(streaming.frames)}")
|
||||
print(f"Final result: {streaming.result}")
|
||||
```
|
||||
|
||||
@@ -114,9 +113,9 @@ async def stream_flow():
|
||||
# Start async streaming
|
||||
streaming = await flow.kickoff_async()
|
||||
|
||||
# Async iteration over chunks
|
||||
async for chunk in streaming:
|
||||
print(chunk.content, end="", flush=True)
|
||||
# Async iteration over stream items
|
||||
async for item in streaming:
|
||||
print(item.content, end="", flush=True)
|
||||
|
||||
# Access final result
|
||||
result = streaming.result
|
||||
@@ -422,7 +421,7 @@ except Exception as e:
|
||||
|
||||
## Cancellation and Resource Cleanup
|
||||
|
||||
`FlowStreamingOutput` supports graceful cancellation so that in-flight work stops promptly when the consumer disconnects.
|
||||
The stream session supports graceful cancellation so that in-flight work stops promptly when the consumer disconnects.
|
||||
|
||||
### Async Context Manager
|
||||
|
||||
@@ -430,8 +429,8 @@ except Exception as e:
|
||||
streaming = await flow.kickoff_async()
|
||||
|
||||
async with streaming:
|
||||
async for chunk in streaming:
|
||||
print(chunk.content, end="", flush=True)
|
||||
async for item in streaming:
|
||||
print(item.content, end="", flush=True)
|
||||
```
|
||||
|
||||
### Explicit Cancellation
|
||||
@@ -439,8 +438,8 @@ async with streaming:
|
||||
```python Code
|
||||
streaming = await flow.kickoff_async()
|
||||
try:
|
||||
async for chunk in streaming:
|
||||
print(chunk.content, end="", flush=True)
|
||||
async for item in streaming:
|
||||
print(item.content, end="", flush=True)
|
||||
finally:
|
||||
await streaming.aclose() # async
|
||||
# streaming.close() # sync equivalent
|
||||
@@ -451,7 +450,7 @@ After cancellation, `streaming.is_cancelled` and `streaming.is_completed` are bo
|
||||
## Important Notes
|
||||
|
||||
- Streaming automatically enables LLM streaming for any crews used within the flow
|
||||
- You must iterate through all chunks before accessing the `.result` property
|
||||
- You must iterate through all stream items before accessing the `.result` property
|
||||
- Streaming works with both structured and unstructured flow state
|
||||
- Flow streaming captures output from all crews and LLM calls in the flow
|
||||
- Each chunk includes context about which agent and task generated it
|
||||
@@ -475,4 +474,4 @@ result = streaming.result
|
||||
print(f"\nFlow complete! View structure at: research_flow.html")
|
||||
```
|
||||
|
||||
By leveraging flow streaming, you can build sophisticated, responsive applications that provide users with real-time visibility into complex multi-stage workflows, making your AI automations more transparent and engaging.
|
||||
By leveraging flow streaming, you can build sophisticated, responsive applications that provide users with real-time visibility into complex multi-stage workflows, making your AI automations more transparent and engaging.
|
||||
|
||||
@@ -28,6 +28,8 @@ frame.timestamp # event timestamp
|
||||
frame.parent_id # parent event id, when available
|
||||
frame.previous_id # previous event id, when available
|
||||
frame.data # event payload
|
||||
frame.event # alias for frame.data
|
||||
frame.content # printable text for token-like frames, otherwise ""
|
||||
```
|
||||
|
||||
The `channel` field is the fastest way to route frames in consumers:
|
||||
@@ -45,7 +47,7 @@ The `channel` field is the fastest way to route frames in consumers:
|
||||
|
||||
## Stream a Flow
|
||||
|
||||
Use `stream_events()` to run a Flow and iterate over all frames:
|
||||
Set `stream=True` on a Flow to make `kickoff()` return a stream session:
|
||||
|
||||
```python
|
||||
from crewai.flow import Flow, start
|
||||
@@ -57,18 +59,22 @@ class ReportFlow(Flow):
|
||||
return "done"
|
||||
|
||||
|
||||
flow = ReportFlow()
|
||||
stream = flow.stream_events()
|
||||
flow = ReportFlow(stream=True)
|
||||
stream = flow.kickoff()
|
||||
|
||||
with stream:
|
||||
for frame in stream.events:
|
||||
print(frame.seq, frame.channel, frame.type, frame.data)
|
||||
for chunk in stream:
|
||||
print(chunk.content, end="", flush=True)
|
||||
if chunk.type == "tool_usage_started":
|
||||
print(chunk.event["tool_name"])
|
||||
|
||||
result = stream.result
|
||||
```
|
||||
|
||||
You must consume the stream before reading `stream.result`. Accessing the result early raises a `RuntimeError` so consumers do not accidentally treat a partial run as complete.
|
||||
|
||||
You can also call `flow.stream_events(...)` directly when you want streaming for a single invocation without setting `stream=True` on the Flow instance.
|
||||
|
||||
## Filter by Channel
|
||||
|
||||
`StreamSession` exposes channel projections that preserve global frame order within the selected channel:
|
||||
@@ -78,7 +84,7 @@ stream = flow.stream_events()
|
||||
|
||||
with stream:
|
||||
for frame in stream.llm:
|
||||
print(frame.data.get("chunk", ""), end="", flush=True)
|
||||
print(frame.content, end="", flush=True)
|
||||
|
||||
result = stream.result
|
||||
```
|
||||
@@ -105,8 +111,8 @@ flow = ReportFlow()
|
||||
stream = flow.astream()
|
||||
|
||||
async with stream:
|
||||
async for frame in stream.events:
|
||||
print(frame.channel, frame.type)
|
||||
async for chunk in stream.events:
|
||||
print(chunk.channel, chunk.type, chunk.content)
|
||||
|
||||
result = stream.result
|
||||
```
|
||||
@@ -115,14 +121,14 @@ The async session has the same projections as the sync session.
|
||||
|
||||
## Stream a Direct LLM Call
|
||||
|
||||
`llm.call(...)` still returns the final assembled result. Use `llm.stream_call(...)` when you want to iterate over chunks as they arrive:
|
||||
`llm.call(...)` still returns the final assembled result. Use `llm.stream_events(...)` when you want to iterate over chunks as they arrive while keeping the structured event payload:
|
||||
|
||||
```python
|
||||
from crewai import LLM
|
||||
|
||||
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
chunks = llm.stream_call(
|
||||
stream = llm.stream_events(
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
@@ -131,26 +137,14 @@ chunks = llm.stream_call(
|
||||
]
|
||||
)
|
||||
|
||||
for chunk in chunks:
|
||||
print(chunk.content, end="", flush=True)
|
||||
|
||||
result = chunks.result
|
||||
```
|
||||
|
||||
Use `llm.stream_events(...)` when a runtime needs the full `StreamFrame` envelope rather than text chunks:
|
||||
|
||||
```python
|
||||
stream = llm.stream_events("Explain CrewAI streaming in two short sentences.")
|
||||
|
||||
with stream:
|
||||
for frame in stream.llm:
|
||||
if frame.type == "llm_stream_chunk":
|
||||
print(frame.data.get("chunk", ""), end="", flush=True)
|
||||
for chunk in stream:
|
||||
print(chunk.content, end="", flush=True)
|
||||
|
||||
result = stream.result
|
||||
```
|
||||
|
||||
Both methods temporarily enable streaming for the wrapped call and restore the LLM's previous `stream` setting afterward. Provider integrations continue to emit the underlying LLM stream events; these helpers provide a common iterator API over those events for every LLM provider.
|
||||
`llm.stream_events(...)` temporarily enables streaming for the wrapped call and restores the LLM's previous `stream` setting afterward. Provider integrations continue to emit the underlying LLM stream events; this helper provides a common iterator API over those events for every LLM provider.
|
||||
|
||||
## Conversational Turns
|
||||
|
||||
@@ -172,7 +166,7 @@ stream = flow.stream_turn("What can you help me with?", session_id="session-1")
|
||||
with stream:
|
||||
for frame in stream.events:
|
||||
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
||||
print(frame.data.get("chunk", ""), end="", flush=True)
|
||||
print(frame.content, end="", flush=True)
|
||||
|
||||
reply = stream.result
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user