Skip to content
Open
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
65 changes: 65 additions & 0 deletions docs/en/concepts/flows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,71 @@ flow.kickoff()

By providing both unstructured and structured state management options, CrewAI Flows empowers developers to build AI workflows that are both flexible and robust, catering to a wide range of application requirements.

## Flow Verbosity Control

The `verbose` parameter allows you to control console output and logging for your Flows. By default, Flows run in quiet mode (`verbose=False`), which suppresses flow-related console panels and log messages. When `verbose=True`, Flows will display detailed execution information, including flow creation panels, method execution status, and flow completion messages.

### Basic Usage

```python
from crewai.flow.flow import Flow, listen, start

class ExampleFlow(Flow):
@start()
def step_1(self):
return "result"

# Quiet mode (default) - no verbose output
flow_quiet = ExampleFlow(verbose=False)
result = flow_quiet.kickoff()

# Verbose mode - displays flow execution details
flow_verbose = ExampleFlow(verbose=True)
result = flow_verbose.kickoff()
```

### Verbose and @persist Decorator

The `verbose` parameter on the Flow instance controls flow-level console output and logging, while the `@persist` decorator has its own `verbose` parameter that controls persistence-specific logging. These operate independently:

```python
from crewai.flow.flow import Flow, listen, start
from crewai.flow.persistence import persist
from crewai.flow.persistence.sqlite import SQLiteFlowPersistence

persistence = SQLiteFlowPersistence("my_flow.db")

# Flow verbose=False: No flow panels/logs, but @persist can still log
class MixedVerbosityFlow(Flow):
@start()
@persist(persistence, verbose=True) # Persistence logging enabled
def init_step(self):
self.state["message"] = "Hello"
return "Initialized"

# Flow runs quietly, but persistence operations are logged
flow = MixedVerbosityFlow(verbose=False)
flow.kickoff()
```

### Verbose vs Tracing

The `verbose` parameter controls console output and logging, while `tracing` controls execution tracing for observability platforms. These are independent settings:

```python
# Verbose output but no tracing
flow1 = ExampleFlow(verbose=True, tracing=False)

# No verbose output but tracing enabled
flow2 = ExampleFlow(verbose=False, tracing=True)

# Both enabled
flow3 = ExampleFlow(verbose=True, tracing=True)

# Both disabled (default)
flow4 = ExampleFlow(verbose=False, tracing=False)
```

## Flow Persistence

The @persist decorator enables automatic state persistence in CrewAI Flows, allowing you to maintain flow state across restarts or different workflow executions. This decorator can be applied at either the class level or method level, providing flexibility in how you manage state persistence.
Expand Down
114 changes: 84 additions & 30 deletions lib/crewai/src/crewai/events/event_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,54 +291,108 @@ def on_lite_agent_execution_error(source, event: LiteAgentExecutionErrorEvent):
@crewai_event_bus.on(FlowCreatedEvent)
def on_flow_created(source, event: FlowCreatedEvent):
self._telemetry.flow_creation_span(event.flow_name)
tree = self.formatter.create_flow_tree(event.flow_name, str(source.flow_id))
self.formatter.current_flow_tree = tree
# Check Flow's verbose flag and temporarily set formatter verbose
flow_verbose = getattr(source, "verbose", False)
with self._crew_tree_lock:
original_verbose = self.formatter.verbose
self.formatter.verbose = flow_verbose
try:
tree = self.formatter.create_flow_tree(event.flow_name, str(source.flow_id))
self.formatter.current_flow_tree = tree
finally:
with self._crew_tree_lock:
self.formatter.verbose = original_verbose

@crewai_event_bus.on(FlowStartedEvent)
def on_flow_started(source, event: FlowStartedEvent):
self._telemetry.flow_execution_span(
event.flow_name, list(source._methods.keys())
)
self.formatter.start_flow(event.flow_name, str(source.flow_id))
# Check Flow's verbose flag and temporarily set formatter verbose
flow_verbose = getattr(source, "verbose", False)
with self._crew_tree_lock:
original_verbose = self.formatter.verbose
self.formatter.verbose = flow_verbose
try:
self.formatter.start_flow(event.flow_name, str(source.flow_id))
finally:
with self._crew_tree_lock:
self.formatter.verbose = original_verbose

@crewai_event_bus.on(FlowFinishedEvent)
def on_flow_finished(source, event: FlowFinishedEvent):
self.formatter.update_flow_status(
self.formatter.current_flow_tree, event.flow_name, source.flow_id
)
# Check Flow's verbose flag and temporarily set formatter verbose
flow_verbose = getattr(source, "verbose", False)
with self._crew_tree_lock:
original_verbose = self.formatter.verbose
self.formatter.verbose = flow_verbose
try:
self.formatter.update_flow_status(
self.formatter.current_flow_tree, event.flow_name, source.flow_id
)
finally:
with self._crew_tree_lock:
self.formatter.verbose = original_verbose

@crewai_event_bus.on(MethodExecutionStartedEvent)
def on_method_execution_started(source, event: MethodExecutionStartedEvent):
method_branch = self.method_branches.get(event.method_name)
updated_branch = self.formatter.update_method_status(
method_branch,
self.formatter.current_flow_tree,
event.method_name,
"running",
)
self.method_branches[event.method_name] = updated_branch
# Check Flow's verbose flag and temporarily set formatter verbose
flow_verbose = getattr(source, "verbose", False)
with self._crew_tree_lock:
original_verbose = self.formatter.verbose
self.formatter.verbose = flow_verbose
try:
method_branch = self.method_branches.get(event.method_name)
updated_branch = self.formatter.update_method_status(
method_branch,
self.formatter.current_flow_tree,
event.method_name,
"running",
)
self.method_branches[event.method_name] = updated_branch
finally:
with self._crew_tree_lock:
self.formatter.verbose = original_verbose

@crewai_event_bus.on(MethodExecutionFinishedEvent)
def on_method_execution_finished(source, event: MethodExecutionFinishedEvent):
method_branch = self.method_branches.get(event.method_name)
updated_branch = self.formatter.update_method_status(
method_branch,
self.formatter.current_flow_tree,
event.method_name,
"completed",
)
self.method_branches[event.method_name] = updated_branch
# Check Flow's verbose flag and temporarily set formatter verbose
flow_verbose = getattr(source, "verbose", False)
with self._crew_tree_lock:
original_verbose = self.formatter.verbose
self.formatter.verbose = flow_verbose
try:
method_branch = self.method_branches.get(event.method_name)
updated_branch = self.formatter.update_method_status(
method_branch,
self.formatter.current_flow_tree,
event.method_name,
"completed",
)
self.method_branches[event.method_name] = updated_branch
finally:
with self._crew_tree_lock:
self.formatter.verbose = original_verbose

@crewai_event_bus.on(MethodExecutionFailedEvent)
def on_method_execution_failed(source, event: MethodExecutionFailedEvent):
method_branch = self.method_branches.get(event.method_name)
updated_branch = self.formatter.update_method_status(
method_branch,
self.formatter.current_flow_tree,
event.method_name,
"failed",
)
self.method_branches[event.method_name] = updated_branch
# Check Flow's verbose flag and temporarily set formatter verbose
flow_verbose = getattr(source, "verbose", False)
with self._crew_tree_lock:
original_verbose = self.formatter.verbose
self.formatter.verbose = flow_verbose
try:
method_branch = self.method_branches.get(event.method_name)
updated_branch = self.formatter.update_method_status(
method_branch,
self.formatter.current_flow_tree,
event.method_name,
"failed",
)
self.method_branches[event.method_name] = updated_branch
finally:
with self._crew_tree_lock:
self.formatter.verbose = original_verbose

# ----------- TOOL USAGE EVENTS -----------

Expand Down
Loading