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 import os
from typing import Any, Dict, Optional from typing import Any, Dict, Optional, Union
from rich.console import Console from rich.console import Console
from rich.panel import Panel from rich.panel import Panel
@@ -8,6 +8,39 @@ from rich.tree import Tree
from rich.live import Live from rich.live import Live
from rich.syntax import Syntax from rich.syntax import Syntax
DEFAULT_EMOJI_MAP = {
"": "[DONE]",
"": "[FAILED]",
"🚀": "[CREW]",
"🔄": "[RUNNING]",
"📋": "[TASK]",
"🔧": "[TOOL]",
"🧠": "[THINKING]",
"🌊": "[FLOW]",
"": "[CREATED]",
"🧪": "[TEST]",
"📚": "[KNOWLEDGE]",
"🔍": "[SEARCH]",
"🔎": "[QUERY]",
"🤖": "[AGENT]",
"📊": "[METRICS]",
"": "[QUICK]",
"🎯": "[TARGET]",
"🔗": "[LINK]",
"💡": "[IDEA]",
"⚠️": "[WARNING]",
"🎉": "[SUCCESS]",
"🔥": "[HOT]",
"💾": "[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: class ConsoleFormatter:
current_crew_tree: Optional[Tree] = None current_crew_tree: Optional[Tree] = None
@@ -25,7 +58,13 @@ class ConsoleFormatter:
def __init__(self, verbose: bool = False): def __init__(self, verbose: bool = False):
self.console = Console(width=None) self.console = Console(width=None)
self.verbose = verbose self.verbose = verbose
self.disable_emojis = os.getenv("CREWAI_DISABLE_EMOJIS", "").lower() in ("true", "1", "yes") 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) # Live instance to dynamically update a Tree renderable (e.g. the Crew tree)
# When multiple Tree objects are printed sequentially we reuse this Live # When multiple Tree objects are printed sequentially we reuse this Live
# instance so the previous render is replaced instead of writing a new one. # instance so the previous render is replaced instead of writing a new one.
@@ -33,37 +72,15 @@ class ConsoleFormatter:
# final Tree persists on the terminal. # final Tree persists on the terminal.
self._live: Optional[Live] = None self._live: Optional[Live] = None
EMOJI_MAP = {
"": "[DONE]",
"": "[FAILED]",
"🚀": "[CREW]",
"🔄": "[RUNNING]",
"📋": "[TASK]",
"🔧": "[TOOL]",
"🧠": "[THINKING]",
"🌊": "[FLOW]",
"": "[CREATED]",
"🧪": "[TEST]",
"📚": "[KNOWLEDGE]",
"🔍": "[SEARCH]",
"🔎": "[QUERY]",
"🤖": "[AGENT]",
"📊": "[METRICS]",
"": "[QUICK]",
"🎯": "[TARGET]",
"🔗": "[LINK]",
"💡": "[IDEA]",
"⚠️": "[WARNING]",
"🎉": "[SUCCESS]",
"🔥": "[HOT]",
"💾": "[SAVE]",
"🔒": "[SECURE]",
"🌟": "[STAR]",
}
def _get_icon(self, emoji: str) -> str: def _get_icon(self, emoji: str) -> str:
"""Get emoji or text alternative based on disable_emojis setting."""
if self.disable_emojis: 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 return emoji
def create_panel(self, content: Text, title: str, style: str = "blue") -> Panel: 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 assert formatter._get_icon(emoji) == expected_text
def test_unknown_emoji_fallback(self): 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"}): with patch.dict(os.environ, {"CREWAI_DISABLE_EMOJIS": "true"}):
formatter = ConsoleFormatter(verbose=True) formatter = ConsoleFormatter(verbose=True)
result = formatter._get_icon("🦄") result = formatter._get_icon("🦄")
assert result == "" assert result == "[ICON:UNKNOWN]"
@patch.dict(os.environ, {"CREWAI_DISABLE_EMOJIS": "true"}) @patch.dict(os.environ, {"CREWAI_DISABLE_EMOJIS": "true"})
def test_crew_tree_creation_without_emojis(self): def test_crew_tree_creation_without_emojis(self):