Fix verbose output to show task names instead of UUIDs

- Modified event listener to pass task objects instead of IDs to console formatter
- Updated console formatter methods to display task names with ID fallback
- Added helper method _get_task_display_name with fallback logic:
  1. Task name + partial ID: 'Research Analysis (ID: 12345678...)'
  2. Description + partial ID: 'Analyze market trends... (ID: 12345678...)'
  3. Full ID as final fallback: '12345678-1234-5678-9012-123456789abc'
- Added comprehensive tests for verbose output functionality

Fixes #3310

Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
Devin AI
2025-08-13 12:55:13 +00:00
parent a0eadf783b
commit 70104e9651
3 changed files with 121 additions and 10 deletions

View File

@@ -162,7 +162,7 @@ class EventListener(BaseEventListener):
span = self._telemetry.task_started(crew=source.agent.crew, task=source)
self.execution_spans[source] = span
self.formatter.create_task_branch(
self.formatter.current_crew_tree, source.id
self.formatter.current_crew_tree, source
)
@crewai_event_bus.on(TaskCompletedEvent)
@@ -175,7 +175,7 @@ class EventListener(BaseEventListener):
self.formatter.update_task_status(
self.formatter.current_crew_tree,
source.id,
source,
source.agent.role,
"completed",
)
@@ -190,7 +190,7 @@ class EventListener(BaseEventListener):
self.formatter.update_task_status(
self.formatter.current_crew_tree,
source.id,
source,
source.agent.role,
"failed",
)

View File

@@ -220,14 +220,16 @@ class ConsoleFormatter:
return tree
def create_task_branch(
self, crew_tree: Optional[Tree], task_id: str
self, crew_tree: Optional[Tree], task: Any
) -> Optional[Tree]:
"""Create and initialize a task branch."""
if not self.verbose:
return None
task_display = self._get_task_display_name(task)
task_content = Text()
task_content.append(f"📋 Task: {task_id}", style="yellow bold")
task_content.append(f"📋 Task: {task_display}", style="yellow bold")
task_content.append("\nStatus: ", style="white")
task_content.append("Executing Task...", style="yellow dim")
@@ -248,7 +250,7 @@ class ConsoleFormatter:
def update_task_status(
self,
crew_tree: Optional[Tree],
task_id: str,
task: Any,
agent_role: str,
status: str = "completed",
) -> None:
@@ -256,6 +258,9 @@ class ConsoleFormatter:
if not self.verbose or crew_tree is None:
return
task_display = self._get_task_display_name(task)
task_id = str(task.id)
if status == "completed":
style = "green"
status_text = "✅ Completed"
@@ -267,11 +272,11 @@ class ConsoleFormatter:
# Update tree label
for branch in crew_tree.children:
if str(task_id) in str(branch.label):
if task_id in str(branch.label):
# Build label without introducing stray blank lines
task_content = Text()
# First line: Task ID
task_content.append(f"📋 Task: {task_id}", style=f"{style} bold")
# First line: Task name/description
task_content.append(f"📋 Task: {task_display}", style=f"{style} bold")
# Second line: Assigned to
task_content.append("\nAssigned to: ", style="white")
@@ -286,7 +291,7 @@ class ConsoleFormatter:
# Show status panel
content = self.create_status_content(
f"Task {status.title()}", str(task_id), style, Agent=agent_role
f"Task {status.title()}", task_display, style, Agent=agent_role
)
self.print_panel(content, panel_title, style)
@@ -1754,3 +1759,13 @@ class ConsoleFormatter:
Attempts=f"{retry_count + 1}",
)
self.print_panel(content, "🛡️ Guardrail Failed", "red")
def _get_task_display_name(self, task: Any) -> str:
"""Get display name for a task, with fallback logic."""
if hasattr(task, 'name') and task.name:
return f"{task.name} (ID: {str(task.id)[:8]}...)"
elif hasattr(task, 'description') and task.description:
desc = task.description[:50] + "..." if len(task.description) > 50 else task.description
return f"{desc} (ID: {str(task.id)[:8]}...)"
else:
return str(task.id)

View File

@@ -0,0 +1,96 @@
import pytest
from unittest.mock import Mock, patch
from crewai.utilities.events.utils.console_formatter import ConsoleFormatter
class TestConsoleFormatterVerbose:
"""Test verbose output functionality in console formatter."""
def setup_method(self):
"""Set up test fixtures."""
self.formatter = ConsoleFormatter(verbose=True)
def test_get_task_display_name_with_name(self):
"""Test task display name when task has a name."""
task = Mock()
task.name = "Research Market Trends"
task.id = "12345678-1234-5678-9012-123456789abc"
result = self.formatter._get_task_display_name(task)
assert "Research Market Trends" in result
assert "12345678" in result
def test_get_task_display_name_with_description_only(self):
"""Test task display name when task has no name but has description."""
task = Mock()
task.name = None
task.description = "Analyze current market trends and provide insights"
task.id = "12345678-1234-5678-9012-123456789abc"
result = self.formatter._get_task_display_name(task)
assert "Analyze current market trends" in result
assert "12345678" in result
def test_get_task_display_name_long_description_truncated(self):
"""Test task display name truncates long descriptions."""
task = Mock()
task.name = None
task.description = "This is a very long task description that should be truncated because it exceeds the maximum length"
task.id = "12345678-1234-5678-9012-123456789abc"
result = self.formatter._get_task_display_name(task)
assert len(result.split("(ID:")[0].strip()) <= 53
assert "..." in result
def test_get_task_display_name_fallback_to_id(self):
"""Test task display name falls back to ID when no name or description."""
task = Mock()
task.name = None
task.description = None
task.id = "12345678-1234-5678-9012-123456789abc"
result = self.formatter._get_task_display_name(task)
assert result == "12345678-1234-5678-9012-123456789abc"
@patch('crewai.utilities.events.utils.console_formatter.ConsoleFormatter.print')
def test_create_task_branch_uses_task_name(self, mock_print):
"""Test create_task_branch displays task name instead of ID."""
task = Mock()
task.name = "Write Blog Post"
task.id = "12345678-1234-5678-9012-123456789abc"
crew_tree = Mock()
crew_tree.add.return_value = Mock()
self.formatter.create_task_branch(crew_tree, task)
call_args = crew_tree.add.call_args[0][0]
assert "Write Blog Post" in str(call_args)
assert "12345678" in str(call_args)
@patch('crewai.utilities.events.utils.console_formatter.ConsoleFormatter.print')
def test_update_task_status_uses_task_name(self, mock_print):
"""Test update_task_status displays task name instead of ID."""
task = Mock()
task.name = "Data Analysis"
task.id = "12345678-1234-5678-9012-123456789abc"
crew_tree = Mock()
branch = Mock()
branch.label = "12345678-1234-5678-9012-123456789abc"
crew_tree.children = [branch]
self.formatter.update_task_status(crew_tree, task, "Data Analyst", "completed")
updated_label = branch.label
assert "Data Analysis" in str(updated_label)
def test_verbose_disabled_returns_none(self):
"""Test that methods return None when verbose is disabled."""
formatter = ConsoleFormatter(verbose=False)
task = Mock()
result = formatter.create_task_branch(Mock(), task)
assert result is None
formatter.update_task_status(Mock(), task, "Agent", "completed")