Compare commits

...

17 Commits

Author SHA1 Message Date
Brandon Hancock (bhancock_ai)
781dee3676 Merge branch 'main' into clean/event-listener-logs 2025-03-13 13:57:55 -04:00
Lorenze Jay
d946005ba2 Enhance flow logging in EventListener and ConsoleFormatter by including flow ID in tree creation and status updates for better traceability. 2025-03-13 10:21:43 -07:00
Lorenze Jay
73801194d3 Remove unused import and clean up type hints in EventListener 2025-03-13 08:16:53 -07:00
Brandon Hancock
09cc07fb7a Keep private parts private 2025-03-13 09:37:49 -04:00
Lorenze Jay
87f9169a39 Refactor crew test to instantiate EventListener and set verbose flags for improved clarity in logging 2025-03-12 13:43:58 -07:00
Lorenze Jay
3f417b2fc1 Enhance EventListener with property setters for crew, task, agent, tool, flow, and method branches to streamline state management 2025-03-12 13:07:37 -07:00
Lorenze Jay
0e521fbf2e Refactor EventListener to utilize ConsoleFormatter for improved logging and visualization 2025-03-12 12:52:38 -07:00
Lorenze Jay
83c5dd1925 Refactor EventListener tree node creation and remove unused method 2025-03-11 15:20:04 -07:00
Lorenze Jay
6e163db427 Simplify EventListener import in Crew class 2025-03-11 15:11:34 -07:00
Lorenze Jay
9dfa8efacb Add type hints to EventListener class attributes 2025-03-11 11:48:28 -07:00
Lorenze Jay
da92412225 Fix EventListener tool branch removal and type hinting 2025-03-11 11:46:08 -07:00
Lorenze Jay
1dc18403be Improve task logging with task ID display in EventListener 2025-03-11 11:37:50 -07:00
Lorenze Jay
e077ffcbbe Refactor EventListener logging and visualization with improved tool usage tracking 2025-03-11 11:37:05 -07:00
Lorenze Jay
51599cc36e Merge branch 'main' of github.com:crewAIInc/crewAI into clean/event-listener-logs 2025-03-11 08:51:24 -07:00
Lorenze Jay
3ffe62c900 Update crew test to set EventListener verbose flag 2025-03-11 08:46:35 -07:00
Lorenze Jay
7c46dce2f7 Add verbose flag to EventListener for controlled logging 2025-03-11 08:43:56 -07:00
Lorenze Jay
f2d53350d8 Enhance Event Listener with Rich Visualization and Improved Logging 2025-03-10 16:43:53 -07:00
5 changed files with 785 additions and 109 deletions

View File

@@ -54,6 +54,7 @@ from crewai.utilities.events.crew_events import (
CrewTrainStartedEvent,
)
from crewai.utilities.events.crewai_event_bus import crewai_event_bus
from crewai.utilities.events.event_listener import EventListener
from crewai.utilities.formatter import (
aggregate_raw_outputs_from_task_outputs,
aggregate_raw_outputs_from_tasks,
@@ -248,7 +249,11 @@ class Crew(BaseModel):
@model_validator(mode="after")
def set_private_attrs(self) -> "Crew":
"""Set private attributes."""
self._cache_handler = CacheHandler()
event_listener = EventListener()
event_listener.verbose = self.verbose
event_listener.formatter.verbose = self.verbose
self._logger = Logger(verbose=self.verbose)
if self.output_log_file:
self._file_handler = FileHandler(self.output_log_file)

View File

@@ -5,6 +5,8 @@ from crewai.utilities.events.crewai_event_bus import CrewAIEventsBus, crewai_eve
class BaseEventListener(ABC):
verbose: bool = False
def __init__(self):
super().__init__()
self.setup_listeners(crewai_event_bus)

View File

@@ -14,6 +14,7 @@ from crewai.utilities.events.llm_events import (
LLMCallStartedEvent,
LLMStreamChunkEvent,
)
from crewai.utilities.events.utils.console_formatter import ConsoleFormatter
from .agent_events import AgentExecutionCompletedEvent, AgentExecutionStartedEvent
from .crew_events import (
@@ -64,82 +65,53 @@ class EventListener(BaseEventListener):
self._telemetry.set_tracer()
self.execution_spans = {}
self._initialized = True
self.formatter = ConsoleFormatter()
# ----------- CREW EVENTS -----------
def setup_listeners(self, crewai_event_bus):
@crewai_event_bus.on(CrewKickoffStartedEvent)
def on_crew_started(source, event: CrewKickoffStartedEvent):
self.logger.log(
f"🚀 Crew '{event.crew_name}' started, {source.id}",
event.timestamp,
)
self.formatter.create_crew_tree(event.crew_name or "Crew", source.id)
self._telemetry.crew_execution_span(source, event.inputs)
@crewai_event_bus.on(CrewKickoffCompletedEvent)
def on_crew_completed(source, event: CrewKickoffCompletedEvent):
# Handle telemetry
final_string_output = event.output.raw
self._telemetry.end_crew(source, final_string_output)
self.logger.log(
f"✅ Crew '{event.crew_name}' completed, {source.id}",
event.timestamp,
self.formatter.update_crew_tree(
self.formatter.current_crew_tree,
event.crew_name or "Crew",
source.id,
"completed",
)
@crewai_event_bus.on(CrewKickoffFailedEvent)
def on_crew_failed(source, event: CrewKickoffFailedEvent):
self.logger.log(
f"❌ Crew '{event.crew_name}' failed, {source.id}",
event.timestamp,
)
@crewai_event_bus.on(CrewTestStartedEvent)
def on_crew_test_started(source, event: CrewTestStartedEvent):
cloned_crew = source.copy()
self._telemetry.test_execution_span(
cloned_crew,
event.n_iterations,
event.inputs,
event.eval_llm or "",
)
self.logger.log(
f"🚀 Crew '{event.crew_name}' started test, {source.id}",
event.timestamp,
)
@crewai_event_bus.on(CrewTestCompletedEvent)
def on_crew_test_completed(source, event: CrewTestCompletedEvent):
self.logger.log(
f"✅ Crew '{event.crew_name}' completed test",
event.timestamp,
)
@crewai_event_bus.on(CrewTestFailedEvent)
def on_crew_test_failed(source, event: CrewTestFailedEvent):
self.logger.log(
f"❌ Crew '{event.crew_name}' failed test",
event.timestamp,
self.formatter.update_crew_tree(
self.formatter.current_crew_tree,
event.crew_name or "Crew",
source.id,
"failed",
)
@crewai_event_bus.on(CrewTrainStartedEvent)
def on_crew_train_started(source, event: CrewTrainStartedEvent):
self.logger.log(
f"📋 Crew '{event.crew_name}' started train",
event.timestamp,
self.formatter.handle_crew_train_started(
event.crew_name or "Crew", str(event.timestamp)
)
@crewai_event_bus.on(CrewTrainCompletedEvent)
def on_crew_train_completed(source, event: CrewTrainCompletedEvent):
self.logger.log(
f"✅ Crew '{event.crew_name}' completed train",
event.timestamp,
self.formatter.handle_crew_train_completed(
event.crew_name or "Crew", str(event.timestamp)
)
@crewai_event_bus.on(CrewTrainFailedEvent)
def on_crew_train_failed(source, event: CrewTrainFailedEvent):
self.logger.log(
f"❌ Crew '{event.crew_name}' failed train",
event.timestamp,
)
self.formatter.handle_crew_train_failed(event.crew_name or "Crew")
# ----------- TASK EVENTS -----------
@@ -147,23 +119,25 @@ class EventListener(BaseEventListener):
def on_task_started(source, event: TaskStartedEvent):
span = self._telemetry.task_started(crew=source.agent.crew, task=source)
self.execution_spans[source] = span
self.logger.log(
f"📋 Task started: {source.description}",
event.timestamp,
self.formatter.create_task_branch(
self.formatter.current_crew_tree, source.id
)
@crewai_event_bus.on(TaskCompletedEvent)
def on_task_completed(source, event: TaskCompletedEvent):
# Handle telemetry
span = self.execution_spans.get(source)
if span:
self._telemetry.task_ended(span, source, source.agent.crew)
self.logger.log(
f"✅ Task completed: {source.description}",
event.timestamp,
)
self.execution_spans[source] = None
self.formatter.update_task_status(
self.formatter.current_crew_tree,
source.id,
source.agent.role,
"completed",
)
@crewai_event_bus.on(TaskFailedEvent)
def on_task_failed(source, event: TaskFailedEvent):
span = self.execution_spans.get(source)
@@ -171,25 +145,30 @@ class EventListener(BaseEventListener):
if source.agent and source.agent.crew:
self._telemetry.task_ended(span, source, source.agent.crew)
self.execution_spans[source] = None
self.logger.log(
f"❌ Task failed: {source.description}",
event.timestamp,
self.formatter.update_task_status(
self.formatter.current_crew_tree,
source.id,
source.agent.role,
"failed",
)
# ----------- AGENT EVENTS -----------
@crewai_event_bus.on(AgentExecutionStartedEvent)
def on_agent_execution_started(source, event: AgentExecutionStartedEvent):
self.logger.log(
f"🤖 Agent '{event.agent.role}' started task",
event.timestamp,
self.formatter.create_agent_branch(
self.formatter.current_task_branch,
event.agent.role,
self.formatter.current_crew_tree,
)
@crewai_event_bus.on(AgentExecutionCompletedEvent)
def on_agent_execution_completed(source, event: AgentExecutionCompletedEvent):
self.logger.log(
f"✅ Agent '{event.agent.role}' completed task",
event.timestamp,
self.formatter.update_agent_status(
self.formatter.current_agent_branch,
event.agent.role,
self.formatter.current_crew_tree,
)
# ----------- FLOW EVENTS -----------
@@ -197,95 +176,98 @@ class EventListener(BaseEventListener):
@crewai_event_bus.on(FlowCreatedEvent)
def on_flow_created(source, event: FlowCreatedEvent):
self._telemetry.flow_creation_span(event.flow_name)
self.logger.log(
f"🌊 Flow Created: '{event.flow_name}'",
event.timestamp,
)
self.formatter.create_flow_tree(event.flow_name, str(source.flow_id))
@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.logger.log(
f"🤖 Flow Started: '{event.flow_name}', {source.flow_id}",
event.timestamp,
)
self.formatter.start_flow(event.flow_name, str(source.flow_id))
@crewai_event_bus.on(FlowFinishedEvent)
def on_flow_finished(source, event: FlowFinishedEvent):
self.logger.log(
f"👍 Flow Finished: '{event.flow_name}', {source.flow_id}",
event.timestamp,
self.formatter.update_flow_status(
self.formatter.current_flow_tree, event.flow_name, source.flow_id
)
@crewai_event_bus.on(MethodExecutionStartedEvent)
def on_method_execution_started(source, event: MethodExecutionStartedEvent):
self.logger.log(
f"🤖 Flow Method Started: '{event.method_name}'",
event.timestamp,
)
@crewai_event_bus.on(MethodExecutionFailedEvent)
def on_method_execution_failed(source, event: MethodExecutionFailedEvent):
self.logger.log(
f"❌ Flow Method Failed: '{event.method_name}'",
event.timestamp,
self.formatter.update_method_status(
self.formatter.current_method_branch,
self.formatter.current_flow_tree,
event.method_name,
"running",
)
@crewai_event_bus.on(MethodExecutionFinishedEvent)
def on_method_execution_finished(source, event: MethodExecutionFinishedEvent):
self.logger.log(
f"👍 Flow Method Finished: '{event.method_name}'",
event.timestamp,
self.formatter.update_method_status(
self.formatter.current_method_branch,
self.formatter.current_flow_tree,
event.method_name,
"completed",
)
@crewai_event_bus.on(MethodExecutionFailedEvent)
def on_method_execution_failed(source, event: MethodExecutionFailedEvent):
self.formatter.update_method_status(
self.formatter.current_method_branch,
self.formatter.current_flow_tree,
event.method_name,
"failed",
)
# ----------- TOOL USAGE EVENTS -----------
@crewai_event_bus.on(ToolUsageStartedEvent)
def on_tool_usage_started(source, event: ToolUsageStartedEvent):
self.logger.log(
f"🤖 Tool Usage Started: '{event.tool_name}'",
event.timestamp,
self.formatter.handle_tool_usage_started(
self.formatter.current_agent_branch,
event.tool_name,
self.formatter.current_crew_tree,
)
@crewai_event_bus.on(ToolUsageFinishedEvent)
def on_tool_usage_finished(source, event: ToolUsageFinishedEvent):
self.logger.log(
f"✅ Tool Usage Finished: '{event.tool_name}'",
event.timestamp,
#
self.formatter.handle_tool_usage_finished(
self.formatter.current_tool_branch,
event.tool_name,
self.formatter.current_crew_tree,
)
@crewai_event_bus.on(ToolUsageErrorEvent)
def on_tool_usage_error(source, event: ToolUsageErrorEvent):
self.logger.log(
f"❌ Tool Usage Error: '{event.tool_name}'",
event.timestamp,
#
self.formatter.handle_tool_usage_error(
self.formatter.current_tool_branch,
event.tool_name,
event.error,
self.formatter.current_crew_tree,
)
# ----------- LLM EVENTS -----------
@crewai_event_bus.on(LLMCallStartedEvent)
def on_llm_call_started(source, event: LLMCallStartedEvent):
self.logger.log(
f"🤖 LLM Call Started",
event.timestamp,
self.formatter.handle_llm_call_started(
self.formatter.current_agent_branch,
self.formatter.current_crew_tree,
)
@crewai_event_bus.on(LLMCallCompletedEvent)
def on_llm_call_completed(source, event: LLMCallCompletedEvent):
self.logger.log(
f"✅ LLM Call Completed",
event.timestamp,
self.formatter.handle_llm_call_completed(
self.formatter.current_tool_branch,
self.formatter.current_agent_branch,
self.formatter.current_crew_tree,
)
@crewai_event_bus.on(LLMCallFailedEvent)
def on_llm_call_failed(source, event: LLMCallFailedEvent):
self.logger.log(
f"❌ LLM call failed: {event.error}",
event.timestamp,
self.formatter.handle_llm_call_failed(
self.formatter.current_tool_branch,
event.error,
self.formatter.current_crew_tree,
)
@crewai_event_bus.on(LLMStreamChunkEvent)
@@ -299,5 +281,30 @@ class EventListener(BaseEventListener):
print(content, end="", flush=True)
self.next_chunk = self.text_stream.tell()
@crewai_event_bus.on(CrewTestStartedEvent)
def on_crew_test_started(source, event: CrewTestStartedEvent):
cloned_crew = source.copy()
self._telemetry.test_execution_span(
cloned_crew,
event.n_iterations,
event.inputs,
event.eval_llm or "",
)
self.formatter.handle_crew_test_started(
event.crew_name or "Crew", source.id, event.n_iterations
)
@crewai_event_bus.on(CrewTestCompletedEvent)
def on_crew_test_completed(source, event: CrewTestCompletedEvent):
self.formatter.handle_crew_test_completed(
self.formatter.current_flow_tree,
event.crew_name or "Crew",
)
@crewai_event_bus.on(CrewTestFailedEvent)
def on_crew_test_failed(source, event: CrewTestFailedEvent):
self.formatter.handle_crew_test_failed(event.crew_name or "Crew")
event_listener = EventListener()

View File

@@ -0,0 +1,658 @@
from typing import Dict, Optional
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
from rich.tree import Tree
class ConsoleFormatter:
current_crew_tree: Optional[Tree] = None
current_task_branch: Optional[Tree] = None
current_agent_branch: Optional[Tree] = None
current_tool_branch: Optional[Tree] = None
current_flow_tree: Optional[Tree] = None
current_method_branch: Optional[Tree] = None
tool_usage_counts: Dict[str, int] = {}
def __init__(self, verbose: bool = False):
self.console = Console(width=None)
self.verbose = verbose
def create_panel(self, content: Text, title: str, style: str = "blue") -> Panel:
"""Create a standardized panel with consistent styling."""
return Panel(
content,
title=title,
border_style=style,
padding=(1, 2),
)
def create_status_content(
self, title: str, name: str, status_style: str = "blue", **fields
) -> Text:
"""Create standardized status content with consistent formatting."""
content = Text()
content.append(f"{title}\n", style=f"{status_style} bold")
content.append("Name: ", style="white")
content.append(f"{name}\n", style=status_style)
for label, value in fields.items():
content.append(f"{label}: ", style="white")
content.append(
f"{value}\n", style=fields.get(f"{label}_style", status_style)
)
return content
def update_tree_label(
self,
tree: Tree,
prefix: str,
name: str,
style: str = "blue",
status: Optional[str] = None,
) -> None:
"""Update tree label with consistent formatting."""
label = Text()
label.append(f"{prefix} ", style=f"{style} bold")
label.append(name, style=style)
if status:
label.append("\n Status: ", style="white")
label.append(status, style=f"{style} bold")
tree.label = label
def add_tree_node(self, parent: Tree, text: str, style: str = "yellow") -> Tree:
"""Add a node to the tree with consistent styling."""
return parent.add(Text(text, style=style))
def print(self, *args, **kwargs) -> None:
"""Print to console with consistent formatting if verbose is enabled."""
self.console.print(*args, **kwargs)
def print_panel(
self, content: Text, title: str, style: str = "blue", is_flow: bool = False
) -> None:
"""Print a panel with consistent formatting if verbose is enabled."""
panel = self.create_panel(content, title, style)
if is_flow:
self.print(panel)
self.print()
else:
if self.verbose:
self.print(panel)
self.print()
def update_crew_tree(
self,
tree: Optional[Tree],
crew_name: str,
source_id: str,
status: str = "completed",
) -> None:
"""Handle crew tree updates with consistent formatting."""
if not self.verbose or tree is None:
return
if status == "completed":
prefix, style = "✅ Crew:", "green"
title = "Crew Completion"
content_title = "Crew Execution Completed"
elif status == "failed":
prefix, style = "❌ Crew:", "red"
title = "Crew Failure"
content_title = "Crew Execution Failed"
else:
prefix, style = "🚀 Crew:", "cyan"
title = "Crew Execution"
content_title = "Crew Execution Started"
self.update_tree_label(
tree,
prefix,
crew_name or "Crew",
style,
)
content = self.create_status_content(
content_title,
crew_name or "Crew",
style,
ID=source_id,
)
self.print_panel(content, title, style)
def create_crew_tree(self, crew_name: str, source_id: str) -> Optional[Tree]:
"""Create and initialize a new crew tree with initial status."""
if not self.verbose:
return None
tree = Tree(
Text("🚀 Crew: ", style="cyan bold") + Text(crew_name, style="cyan")
)
content = self.create_status_content(
"Crew Execution Started",
crew_name,
"cyan",
ID=source_id,
)
self.print_panel(content, "Crew Execution Started", "cyan")
# Set the current_crew_tree attribute directly
self.current_crew_tree = tree
return tree
def create_task_branch(
self, crew_tree: Optional[Tree], task_id: str
) -> Optional[Tree]:
"""Create and initialize a task branch."""
if not self.verbose:
return None
task_content = Text()
task_content.append(f"📋 Task: {task_id}", style="yellow bold")
task_content.append("\n Status: ", style="white")
task_content.append("Executing Task...", style="yellow dim")
task_branch = None
if crew_tree:
task_branch = crew_tree.add(task_content)
self.print(crew_tree)
else:
self.print_panel(task_content, "Task Started", "yellow")
self.print()
# Set the current_task_branch attribute directly
self.current_task_branch = task_branch
return task_branch
def update_task_status(
self,
crew_tree: Optional[Tree],
task_id: str,
agent_role: str,
status: str = "completed",
) -> None:
"""Update task status in the tree."""
if not self.verbose or crew_tree is None:
return
if status == "completed":
style = "green"
status_text = "✅ Completed"
panel_title = "Task Completion"
else:
style = "red"
status_text = "❌ Failed"
panel_title = "Task Failure"
# Update tree label
for branch in crew_tree.children:
if str(task_id) in str(branch.label):
task_content = Text()
task_content.append(f"📋 Task: {task_id}", style=f"{style} bold")
task_content.append("\n Assigned to: ", style="white")
task_content.append(agent_role, style=style)
task_content.append("\n Status: ", style="white")
task_content.append(status_text, style=f"{style} bold")
branch.label = task_content
self.print(crew_tree)
break
# Show status panel
content = self.create_status_content(
f"Task {status.title()}", str(task_id), style, Agent=agent_role
)
self.print_panel(content, panel_title, style)
def create_agent_branch(
self, task_branch: Optional[Tree], agent_role: str, crew_tree: Optional[Tree]
) -> Optional[Tree]:
"""Create and initialize an agent branch."""
if not self.verbose or not task_branch or not crew_tree:
return None
agent_branch = task_branch.add("")
self.update_tree_label(
agent_branch, "🤖 Agent:", agent_role, "green", "In Progress"
)
self.print(crew_tree)
self.print()
# Set the current_agent_branch attribute directly
self.current_agent_branch = agent_branch
return agent_branch
def update_agent_status(
self,
agent_branch: Optional[Tree],
agent_role: str,
crew_tree: Optional[Tree],
status: str = "completed",
) -> None:
"""Update agent status in the tree."""
if not self.verbose or agent_branch is None or crew_tree is None:
return
self.update_tree_label(
agent_branch,
"🤖 Agent:",
agent_role,
"green",
"✅ Completed" if status == "completed" else "❌ Failed",
)
self.print(crew_tree)
self.print()
def create_flow_tree(self, flow_name: str, flow_id: str) -> Optional[Tree]:
"""Create and initialize a flow tree."""
content = self.create_status_content(
"Starting Flow Execution", flow_name, "blue", ID=flow_id
)
self.print_panel(content, "Flow Execution", "blue", is_flow=True)
# Create initial tree with flow ID
flow_label = Text()
flow_label.append("🌊 Flow: ", style="blue bold")
flow_label.append(flow_name, style="blue")
flow_label.append("\n ID: ", style="white")
flow_label.append(flow_id, style="blue")
flow_tree = Tree(flow_label)
self.add_tree_node(flow_tree, "✨ Created", "blue")
self.add_tree_node(flow_tree, "✅ Initialization Complete", "green")
return flow_tree
def start_flow(self, flow_name: str, flow_id: str) -> Optional[Tree]:
"""Initialize a flow execution tree."""
flow_tree = Tree("")
flow_label = Text()
flow_label.append("🌊 Flow: ", style="blue bold")
flow_label.append(flow_name, style="blue")
flow_label.append("\n ID: ", style="white")
flow_label.append(flow_id, style="blue")
flow_tree.label = flow_label
self.add_tree_node(flow_tree, "🧠 Starting Flow...", "yellow")
self.print(flow_tree)
self.print()
self.current_flow_tree = flow_tree
return flow_tree
def update_flow_status(
self,
flow_tree: Optional[Tree],
flow_name: str,
flow_id: str,
status: str = "completed",
) -> None:
"""Update flow status in the tree."""
if flow_tree is None:
return
# Update main flow label
self.update_tree_label(
flow_tree,
"✅ Flow Finished:" if status == "completed" else "❌ Flow Failed:",
flow_name,
"green" if status == "completed" else "red",
)
# Update initialization node status
for child in flow_tree.children:
if "Starting Flow" in str(child.label):
child.label = Text(
(
"✅ Flow Completed"
if status == "completed"
else "❌ Flow Failed"
),
style="green" if status == "completed" else "red",
)
break
content = self.create_status_content(
(
"Flow Execution Completed"
if status == "completed"
else "Flow Execution Failed"
),
flow_name,
"green" if status == "completed" else "red",
ID=flow_id,
)
self.print(flow_tree)
self.print_panel(
content, "Flow Completion", "green" if status == "completed" else "red"
)
def update_method_status(
self,
method_branch: Optional[Tree],
flow_tree: Optional[Tree],
method_name: str,
status: str = "running",
) -> Optional[Tree]:
"""Update method status in the flow tree."""
if not flow_tree:
return None
if status == "running":
prefix, style = "🔄 Running:", "yellow"
elif status == "completed":
prefix, style = "✅ Completed:", "green"
# Update initialization node when a method completes successfully
for child in flow_tree.children:
if "Starting Flow" in str(child.label):
child.label = Text("Flow Method Step", style="white")
break
else:
prefix, style = "❌ Failed:", "red"
# Update initialization node on failure
for child in flow_tree.children:
if "Starting Flow" in str(child.label):
child.label = Text("❌ Flow Step Failed", style="red")
break
if not method_branch:
# Find or create method branch
for branch in flow_tree.children:
if method_name in str(branch.label):
method_branch = branch
break
if not method_branch:
method_branch = flow_tree.add("")
method_branch.label = Text(prefix, style=f"{style} bold") + Text(
f" {method_name}", style=style
)
self.print(flow_tree)
self.print()
return method_branch
def handle_tool_usage_started(
self,
agent_branch: Optional[Tree],
tool_name: str,
crew_tree: Optional[Tree],
) -> Optional[Tree]:
"""Handle tool usage started event."""
if not self.verbose or agent_branch is None or crew_tree is None:
return None
# Update tool usage count
self.tool_usage_counts[tool_name] = self.tool_usage_counts.get(tool_name, 0) + 1
# Find existing tool node or create new one
tool_branch = None
for child in agent_branch.children:
if tool_name in str(child.label):
tool_branch = child
break
if not tool_branch:
tool_branch = agent_branch.add("")
# Update label with current count
self.update_tree_label(
tool_branch,
"🔧",
f"Using {tool_name} ({self.tool_usage_counts[tool_name]})",
"yellow",
)
self.print(crew_tree)
self.print()
# Set the current_tool_branch attribute directly
self.current_tool_branch = tool_branch
return tool_branch
def handle_tool_usage_finished(
self,
tool_branch: Optional[Tree],
tool_name: str,
crew_tree: Optional[Tree],
) -> None:
"""Handle tool usage finished event."""
if not self.verbose or tool_branch is None or crew_tree is None:
return
self.update_tree_label(
tool_branch,
"🔧",
f"Used {tool_name} ({self.tool_usage_counts[tool_name]})",
"green",
)
self.print(crew_tree)
self.print()
def handle_tool_usage_error(
self,
tool_branch: Optional[Tree],
tool_name: str,
error: str,
crew_tree: Optional[Tree],
) -> None:
"""Handle tool usage error event."""
if not self.verbose:
return
if tool_branch:
self.update_tree_label(
tool_branch,
"🔧 Failed",
f"{tool_name} ({self.tool_usage_counts[tool_name]})",
"red",
)
self.print(crew_tree)
self.print()
# Show error panel
error_content = self.create_status_content(
"Tool Usage Failed", tool_name, "red", Error=error
)
self.print_panel(error_content, "Tool Error", "red")
def handle_llm_call_started(
self,
agent_branch: Optional[Tree],
crew_tree: Optional[Tree],
) -> Optional[Tree]:
"""Handle LLM call started event."""
if not self.verbose or agent_branch is None or crew_tree is None:
return None
# Only add thinking status if it doesn't exist
if not any("Thinking" in str(child.label) for child in agent_branch.children):
tool_branch = agent_branch.add("")
self.update_tree_label(tool_branch, "🧠", "Thinking...", "blue")
self.print(crew_tree)
self.print()
# Set the current_tool_branch attribute directly
self.current_tool_branch = tool_branch
return tool_branch
return None
def handle_llm_call_completed(
self,
tool_branch: Optional[Tree],
agent_branch: Optional[Tree],
crew_tree: Optional[Tree],
) -> None:
"""Handle LLM call completed event."""
if (
not self.verbose
or tool_branch is None
or agent_branch is None
or crew_tree is None
):
return
# Remove the thinking status node when complete
if "Thinking" in str(tool_branch.label):
agent_branch.children.remove(tool_branch)
self.print(crew_tree)
self.print()
def handle_llm_call_failed(
self, tool_branch: Optional[Tree], error: str, crew_tree: Optional[Tree]
) -> None:
"""Handle LLM call failed event."""
if not self.verbose:
return
# Update tool branch if it exists
if tool_branch:
tool_branch.label = Text("❌ LLM Failed", style="red bold")
self.print(crew_tree)
self.print()
# Show error panel
error_content = Text()
error_content.append("❌ LLM Call Failed\n", style="red bold")
error_content.append("Error: ", style="white")
error_content.append(str(error), style="red")
self.print_panel(error_content, "LLM Error", "red")
def handle_crew_test_started(
self, crew_name: str, source_id: str, n_iterations: int
) -> Optional[Tree]:
"""Handle crew test started event."""
if not self.verbose:
return None
# Create initial panel
content = Text()
content.append("🧪 Starting Crew Test\n\n", style="blue bold")
content.append("Crew: ", style="white")
content.append(f"{crew_name}\n", style="blue")
content.append("ID: ", style="white")
content.append(str(source_id), style="blue")
content.append("\nIterations: ", style="white")
content.append(str(n_iterations), style="yellow")
self.print()
self.print_panel(content, "Test Execution", "blue")
self.print()
# Create and display the test tree
test_label = Text()
test_label.append("🧪 Test: ", style="blue bold")
test_label.append(crew_name or "Crew", style="blue")
test_label.append("\n Status: ", style="white")
test_label.append("In Progress", style="yellow")
test_tree = Tree(test_label)
self.add_tree_node(test_tree, "🔄 Running tests...", "yellow")
self.print(test_tree)
self.print()
return test_tree
def handle_crew_test_completed(
self, flow_tree: Optional[Tree], crew_name: str
) -> None:
"""Handle crew test completed event."""
if not self.verbose:
return
if flow_tree:
# Update test tree label to show completion
test_label = Text()
test_label.append("✅ Test: ", style="green bold")
test_label.append(crew_name or "Crew", style="green")
test_label.append("\n Status: ", style="white")
test_label.append("Completed", style="green bold")
flow_tree.label = test_label
# Update the running tests node
for child in flow_tree.children:
if "Running tests" in str(child.label):
child.label = Text("✅ Tests completed successfully", style="green")
self.print(flow_tree)
self.print()
# Create completion panel
completion_content = Text()
completion_content.append("Test Execution Completed\n", style="green bold")
completion_content.append("Crew: ", style="white")
completion_content.append(f"{crew_name}\n", style="green")
completion_content.append("Status: ", style="white")
completion_content.append("Completed", style="green")
self.print_panel(completion_content, "Test Completion", "green")
def handle_crew_train_started(self, crew_name: str, timestamp: str) -> None:
"""Handle crew train started event."""
if not self.verbose:
return
content = Text()
content.append("📋 Crew Training Started\n", style="blue bold")
content.append("Crew: ", style="white")
content.append(f"{crew_name}\n", style="blue")
content.append("Time: ", style="white")
content.append(timestamp, style="blue")
self.print_panel(content, "Training Started", "blue")
self.print()
def handle_crew_train_completed(self, crew_name: str, timestamp: str) -> None:
"""Handle crew train completed event."""
if not self.verbose:
return
content = Text()
content.append("✅ Crew Training Completed\n", style="green bold")
content.append("Crew: ", style="white")
content.append(f"{crew_name}\n", style="green")
content.append("Time: ", style="white")
content.append(timestamp, style="green")
self.print_panel(content, "Training Completed", "green")
self.print()
def handle_crew_train_failed(self, crew_name: str) -> None:
"""Handle crew train failed event."""
if not self.verbose:
return
failure_content = Text()
failure_content.append("❌ Crew Training Failed\n", style="red bold")
failure_content.append("Crew: ", style="white")
failure_content.append(crew_name or "Crew", style="red")
self.print_panel(failure_content, "Training Failure", "red")
self.print()
def handle_crew_test_failed(self, crew_name: str) -> None:
"""Handle crew test failed event."""
if not self.verbose:
return
failure_content = Text()
failure_content.append("❌ Crew Test Failed\n", style="red bold")
failure_content.append("Crew: ", style="white")
failure_content.append(crew_name or "Crew", style="red")
self.print_panel(failure_content, "Test Failure", "red")
self.print()

View File

@@ -33,6 +33,7 @@ from crewai.utilities.events.crew_events import (
CrewTestCompletedEvent,
CrewTestStartedEvent,
)
from crewai.utilities.events.event_listener import EventListener
from crewai.utilities.rpm_controller import RPMController
from crewai.utilities.task_output_storage_handler import TaskOutputStorageHandler
@@ -862,6 +863,9 @@ def test_crew_verbose_output(capsys):
# Now test with verbose set to False
crew.verbose = False
crew._logger = Logger(verbose=False)
event_listener = EventListener()
event_listener.verbose = False
event_listener.formatter.verbose = False
crew.kickoff()
captured = capsys.readouterr()
filtered_output = "\n".join(