mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-03 14:09:24 +00:00
210 lines
6.1 KiB
Plaintext
210 lines
6.1 KiB
Plaintext
---
|
|
title: Consuming Streams
|
|
description: Print LLM chunks, observe tool events, and read final results from CrewAI streams.
|
|
icon: square-terminal
|
|
mode: "wide"
|
|
---
|
|
|
|
## Overview
|
|
|
|
Use this guide when you want to subscribe to a CrewAI stream and print or route frames as they arrive.
|
|
|
|
The basic pattern is:
|
|
|
|
```python
|
|
stream = flow.stream_events(inputs={"topic": "AI agents"})
|
|
|
|
with stream:
|
|
for frame in stream:
|
|
...
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
Always consume the stream before reading `stream.result`.
|
|
|
|
## Print LLM Output
|
|
|
|
If you only care about text generated by LLM calls, subscribe to the `llm` projection and print `frame.content`:
|
|
|
|
```python
|
|
stream = flow.stream_events(inputs={"topic": "AI agents"})
|
|
|
|
with stream:
|
|
for frame in stream.llm:
|
|
print(frame.content, end="", flush=True)
|
|
|
|
print()
|
|
result = stream.result
|
|
```
|
|
|
|
`frame.content` is an empty string for frames that do not carry printable text, so this is also safe:
|
|
|
|
```python
|
|
with flow.stream_events(inputs={"topic": "AI agents"}) as stream:
|
|
for frame in stream.events:
|
|
if frame.channel == "llm" and frame.content:
|
|
print(frame.content, end="", flush=True)
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
## Print Tool Activity
|
|
|
|
Tool events arrive on the `tools` channel. Use `frame.type` to distinguish starts, finishes, and errors.
|
|
|
|
```python
|
|
with flow.stream_events(inputs={"topic": "AI agents"}) as stream:
|
|
for frame in stream.events:
|
|
if frame.channel == "llm" and frame.content:
|
|
print(frame.content, end="", flush=True)
|
|
|
|
if frame.channel == "tools" and frame.type == "tool_usage_started":
|
|
print(f"\nTool started: {frame.event.get('tool_name')}")
|
|
|
|
if frame.channel == "tools" and frame.type == "tool_usage_finished":
|
|
print(f"\nTool finished: {frame.event.get('tool_name')}")
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
`frame.event` is the structured payload for the source event. Use it for metadata such as tool names, arguments, message roles, and runtime identifiers.
|
|
|
|
## Print Streamed Tool Call Arguments
|
|
|
|
When a model supports streamed tool calling, CrewAI emits partial tool-call arguments as `llm_stream_chunk` frames on the `llm` channel. These frames are different from tool execution events:
|
|
|
|
- `llm` channel: the model is constructing the tool call.
|
|
- `tools` channel: CrewAI is executing the tool.
|
|
|
|
The current delta is stored in `frame.event["chunk"]`. The accumulated arguments are stored in `frame.event["tool_call"]["function"]["arguments"]`.
|
|
|
|
```python
|
|
with llm.stream_events("Get the weather in Paris.", tools=[weather_tool]) as stream:
|
|
for frame in stream.interleave(["llm", "tools"]):
|
|
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
|
tool_call = frame.event.get("tool_call")
|
|
if tool_call:
|
|
function = tool_call["function"]
|
|
print(f"\nPreparing tool: {function['name']}")
|
|
print(f"Arguments so far: {function['arguments']}")
|
|
elif frame.content:
|
|
print(frame.content, end="", flush=True)
|
|
|
|
elif frame.channel == "tools" and frame.type == "tool_usage_started":
|
|
print(f"\nRunning tool: {frame.event.get('tool_name')}")
|
|
|
|
elif frame.channel == "tools" and frame.type == "tool_usage_finished":
|
|
print(f"\nTool finished: {frame.event.get('tool_name')}")
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
Some providers emit arguments in small JSON fragments. For display, prefer the accumulated argument string over the latest delta.
|
|
|
|
## Watch Flow Progress
|
|
|
|
Flow lifecycle and method execution frames arrive on the `flow` channel:
|
|
|
|
```python
|
|
with flow.stream_events(inputs={"topic": "AI agents"}) as stream:
|
|
for frame in stream.flow:
|
|
print(frame.type, frame.namespace)
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
Use this when you want a progress log instead of token-level output.
|
|
|
|
## Interleave Selected Channels
|
|
|
|
Use `interleave()` when you want a subset of channels while preserving their relative order:
|
|
|
|
```python
|
|
with flow.stream_events(inputs={"topic": "AI agents"}) as stream:
|
|
for frame in stream.interleave(["llm", "tools"]):
|
|
if frame.channel == "llm":
|
|
print(frame.content, end="", flush=True)
|
|
elif frame.type == "tool_usage_started":
|
|
print(f"\nTool: {frame.event.get('tool_name')}")
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
## Stream a Direct LLM Call
|
|
|
|
Direct `llm.call(...)` returns the final assembled result. To stream a direct LLM call, use `llm.stream_events(...)`:
|
|
|
|
```python
|
|
from crewai import LLM
|
|
|
|
|
|
llm = LLM(model="gpt-4o-mini")
|
|
stream = llm.stream_events("Explain streaming in one sentence.")
|
|
|
|
with stream:
|
|
for frame in stream.llm:
|
|
print(frame.content, end="", flush=True)
|
|
|
|
print()
|
|
result = stream.result
|
|
```
|
|
|
|
## Stream a Conversational Turn
|
|
|
|
Conversational Flows expose `stream_turn()` for one user message:
|
|
|
|
```python
|
|
stream = flow.stream_turn(
|
|
"What can you help me with?",
|
|
session_id="session-1",
|
|
)
|
|
|
|
with stream:
|
|
for frame in stream.interleave(["llm", "messages"]):
|
|
if frame.channel == "llm":
|
|
print(frame.content, end="", flush=True)
|
|
elif frame.channel == "messages":
|
|
print(f"\n{frame.event.get('role')}: {frame.event.get('content')}")
|
|
|
|
reply = stream.result
|
|
```
|
|
|
|
## Async Consumers
|
|
|
|
Async streams use the same channel projections:
|
|
|
|
```python
|
|
stream = flow.astream(inputs={"topic": "AI agents"})
|
|
|
|
async with stream:
|
|
async for frame in stream.llm:
|
|
print(frame.content, end="", flush=True)
|
|
|
|
result = stream.result
|
|
```
|
|
|
|
## Cleanup
|
|
|
|
Use the stream as a context manager when possible. If a client disconnects or you stop consuming early, close the stream:
|
|
|
|
```python
|
|
stream = flow.stream_events(inputs={"topic": "AI agents"})
|
|
|
|
try:
|
|
for frame in stream.events:
|
|
print(frame.content, end="", flush=True)
|
|
finally:
|
|
if not stream.is_exhausted:
|
|
stream.close()
|
|
```
|
|
|
|
For async streams, call `await stream.aclose()`.
|
|
|
|
## See Also
|
|
|
|
- [Streaming](/edge/en/concepts/streaming)
|
|
- [Streaming Runtime Contract](/edge/en/learn/streaming-runtime-contract)
|
|
- [Streaming Flow Execution](/edge/en/learn/streaming-flow-execution)
|
|
- [Streaming Crew Execution](/edge/en/learn/streaming-crew-execution)
|