diff --git a/src/crewai/crew.py b/src/crewai/crew.py index 9cecfed3a..d3a6870dc 100644 --- a/src/crewai/crew.py +++ b/src/crewai/crew.py @@ -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) diff --git a/src/crewai/utilities/events/base_event_listener.py b/src/crewai/utilities/events/base_event_listener.py index 37763dcc1..f08b70025 100644 --- a/src/crewai/utilities/events/base_event_listener.py +++ b/src/crewai/utilities/events/base_event_listener.py @@ -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) diff --git a/src/crewai/utilities/events/event_listener.py b/src/crewai/utilities/events/event_listener.py index c5c049bc6..897ea4a92 100644 --- a/src/crewai/utilities/events/event_listener.py +++ b/src/crewai/utilities/events/event_listener.py @@ -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() diff --git a/src/crewai/utilities/events/utils/console_formatter.py b/src/crewai/utilities/events/utils/console_formatter.py new file mode 100644 index 000000000..3d3e03149 --- /dev/null +++ b/src/crewai/utilities/events/utils/console_formatter.py @@ -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() diff --git a/tests/crew_test.py b/tests/crew_test.py index 6c4b96e37..39a3e9a08 100644 --- a/tests/crew_test.py +++ b/tests/crew_test.py @@ -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(