refactor: Implement code review suggestions for emoji disable feature

- Move EMOJI_MAP to module level as DEFAULT_EMOJI_MAP for memory efficiency
- Add _parse_bool_env() helper function for cleaner boolean conversion
- Implement performance optimization with icon caching
- Improve error handling for unknown emojis with better fallback format
- Update tests to work with new module structure
- All functionality preserved and tests passing

Addresses feedback from PR review #3063

Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
Devin AI
2025-06-25 13:02:32 +00:00
parent 4e288e4cf7
commit a9823a4368
2 changed files with 50 additions and 33 deletions

View File

@@ -1,5 +1,5 @@
import os
from typing import Any, Dict, Optional
from typing import Any, Dict, Optional, Union
from rich.console import Console
from rich.panel import Panel
@@ -8,32 +8,7 @@ from rich.tree import Tree
from rich.live import Live
from rich.syntax import Syntax
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
current_lite_agent_branch: Optional[Tree] = None
tool_usage_counts: Dict[str, int] = {}
current_reasoning_branch: Optional[Tree] = None # Track reasoning status
_live_paused: bool = False
current_llm_tool_tree: Optional[Tree] = None
def __init__(self, verbose: bool = False):
self.console = Console(width=None)
self.verbose = verbose
self.disable_emojis = os.getenv("CREWAI_DISABLE_EMOJIS", "").lower() in ("true", "1", "yes")
# Live instance to dynamically update a Tree renderable (e.g. the Crew tree)
# When multiple Tree objects are printed sequentially we reuse this Live
# instance so the previous render is replaced instead of writing a new one.
# Once any non-Tree renderable is printed we stop the Live session so the
# final Tree persists on the terminal.
self._live: Optional[Live] = None
EMOJI_MAP = {
DEFAULT_EMOJI_MAP = {
"": "[DONE]",
"": "[FAILED]",
"🚀": "[CREW]",
@@ -59,11 +34,53 @@ class ConsoleFormatter:
"💾": "[SAVE]",
"🔒": "[SECURE]",
"🌟": "[STAR]",
}
}
def _parse_bool_env(val: str) -> bool:
"""Parse environment variable value to boolean."""
return val.lower() in ("true", "1", "yes") if val else False
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
current_lite_agent_branch: Optional[Tree] = None
tool_usage_counts: Dict[str, int] = {}
current_reasoning_branch: Optional[Tree] = None # Track reasoning status
_live_paused: bool = False
current_llm_tool_tree: Optional[Tree] = None
def __init__(self, verbose: bool = False):
self.console = Console(width=None)
self.verbose = verbose
self.disable_emojis = _parse_bool_env(os.getenv("CREWAI_DISABLE_EMOJIS", ""))
self.emoji_map = DEFAULT_EMOJI_MAP.copy()
self.icon_cache: Dict[str, str] = {}
if self.disable_emojis:
self.icon_cache = {emoji: text for emoji, text in self.emoji_map.items()}
# Live instance to dynamically update a Tree renderable (e.g. the Crew tree)
# When multiple Tree objects are printed sequentially we reuse this Live
# instance so the previous render is replaced instead of writing a new one.
# Once any non-Tree renderable is printed we stop the Live session so the
# final Tree persists on the terminal.
self._live: Optional[Live] = None
def _get_icon(self, emoji: str) -> str:
"""Get emoji or text alternative based on disable_emojis setting."""
if self.disable_emojis:
return self.EMOJI_MAP.get(emoji, emoji.encode('ascii', 'ignore').decode('ascii'))
if emoji in self.icon_cache:
return self.icon_cache[emoji]
ascii_fallback = emoji.encode('ascii', 'ignore').decode('ascii')
fallback = f"[ICON:{ascii_fallback or 'UNKNOWN'}]"
self.icon_cache[emoji] = fallback
return fallback
return emoji
def create_panel(self, content: Text, title: str, style: str = "blue") -> Panel:

View File

@@ -96,11 +96,11 @@ class TestConsoleFormatterEmojiDisable:
assert formatter._get_icon(emoji) == expected_text
def test_unknown_emoji_fallback(self):
"""Test that unknown emojis fall back to ASCII-only representation."""
"""Test that unknown emojis fall back to proper representation."""
with patch.dict(os.environ, {"CREWAI_DISABLE_EMOJIS": "true"}):
formatter = ConsoleFormatter(verbose=True)
result = formatter._get_icon("🦄")
assert result == ""
assert result == "[ICON:UNKNOWN]"
@patch.dict(os.environ, {"CREWAI_DISABLE_EMOJIS": "true"})
def test_crew_tree_creation_without_emojis(self):