From ee8b3be8e5ab792cee769e803454839591676719 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 04:52:51 +0000 Subject: [PATCH] fix(tracing): stop nagging users who declined tracing (#5665) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - When user explicitly declined tracing, skip the 'Tracing is disabled' message instead of showing it on every crew/flow execution - Add CREWAI_SUPPRESS_TRACING_MESSAGES env var to let users fully suppress the message - Remove duplicate identical if/else branches in all four _show_tracing_disabled_message implementations - Add 24 tests covering suppression via env var, context var, and user-declined scenarios Co-Authored-By: João --- lib/crewai/src/crewai/crew.py | 14 +- .../listeners/tracing/trace_listener.py | 14 +- .../crewai/events/listeners/tracing/utils.py | 11 +- .../crewai/events/utils/console_formatter.py | 14 +- lib/crewai/src/crewai/flow/flow.py | 14 +- .../test_tracing_message_suppression.py | 259 ++++++++++++++++++ 6 files changed, 289 insertions(+), 37 deletions(-) create mode 100644 lib/crewai/tests/tracing/test_tracing_message_suppression.py diff --git a/lib/crewai/src/crewai/crew.py b/lib/crewai/src/crewai/crew.py index 7631a4c2b..e6881576b 100644 --- a/lib/crewai/src/crewai/crew.py +++ b/lib/crewai/src/crewai/crew.py @@ -2272,17 +2272,13 @@ class Crew(FlowTrackable, BaseModel): if should_suppress_tracing_messages(): return + # Don't nag users who have explicitly declined tracing + if has_user_declined_tracing(): + return + console = Console() - if has_user_declined_tracing(): - message = """Info: Tracing is disabled. - -To enable tracing, do any one of these: -• Set tracing=True in your Crew code -• Set CREWAI_TRACING_ENABLED=true in your project's .env file -• Run: crewai traces enable""" - else: - message = """Info: Tracing is disabled. + message = """Info: Tracing is disabled. To enable tracing, do any one of these: • Set tracing=True in your Crew code diff --git a/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py b/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py index 046bc0f1a..90589ead7 100644 --- a/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py +++ b/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py @@ -868,17 +868,13 @@ class TraceCollectionListener(BaseEventListener): if should_suppress_tracing_messages(): return + # Don't nag users who have explicitly declined tracing + if has_user_declined_tracing(): + return + console = Console() - if has_user_declined_tracing(): - message = """Info: Tracing is disabled. - -To enable tracing, do any one of these: -• Set tracing=True in your Crew/Flow code -• Set CREWAI_TRACING_ENABLED=true in your project's .env file -• Run: crewai traces enable""" - else: - message = """Info: Tracing is disabled. + message = """Info: Tracing is disabled. To enable tracing, do any one of these: • Set tracing=True in your Crew/Flow code diff --git a/lib/crewai/src/crewai/events/listeners/tracing/utils.py b/lib/crewai/src/crewai/events/listeners/tracing/utils.py index 314922870..615e6cd12 100644 --- a/lib/crewai/src/crewai/events/listeners/tracing/utils.py +++ b/lib/crewai/src/crewai/events/listeners/tracing/utils.py @@ -53,10 +53,19 @@ def set_suppress_tracing_messages(suppress: bool) -> object: def should_suppress_tracing_messages() -> bool: """Check if tracing messages should be suppressed. + Checks the context variable first, then falls back to the + CREWAI_SUPPRESS_TRACING_MESSAGES environment variable. + Returns: True if messages should be suppressed, False otherwise. """ - return _suppress_tracing_messages.get() + if _suppress_tracing_messages.get(): + return True + return os.getenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "false").lower() in ( + "true", + "1", + "yes", + ) def should_enable_tracing(*, override: bool | None = None) -> bool: diff --git a/lib/crewai/src/crewai/events/utils/console_formatter.py b/lib/crewai/src/crewai/events/utils/console_formatter.py index 7879a4d93..c898b84ac 100644 --- a/lib/crewai/src/crewai/events/utils/console_formatter.py +++ b/lib/crewai/src/crewai/events/utils/console_formatter.py @@ -145,16 +145,12 @@ To update, run: uv sync --upgrade-package crewai""" if listener and listener.first_time_handler.is_first_time: return - if not is_tracing_enabled_in_context(): - if has_user_declined_tracing(): - message = """Info: Tracing is disabled. + # Don't nag users who have explicitly declined tracing + if has_user_declined_tracing(): + return -To enable tracing, do any one of these: -• Set tracing=True in your Crew/Flow code -• Set CREWAI_TRACING_ENABLED=true in your project's .env file -• Run: crewai traces enable""" - else: - message = """Info: Tracing is disabled. + if not is_tracing_enabled_in_context(): + message = """Info: Tracing is disabled. To enable tracing, do any one of these: • Set tracing=True in your Crew/Flow code diff --git a/lib/crewai/src/crewai/flow/flow.py b/lib/crewai/src/crewai/flow/flow.py index 95e6a9a15..e95bb7526 100644 --- a/lib/crewai/src/crewai/flow/flow.py +++ b/lib/crewai/src/crewai/flow/flow.py @@ -3546,17 +3546,13 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta): if should_suppress_tracing_messages(): return + # Don't nag users who have explicitly declined tracing + if has_user_declined_tracing(): + return + console = Console() - if has_user_declined_tracing(): - message = """Info: Tracing is disabled. - -To enable tracing, do any one of these: -• Set tracing=True in your Flow code -• Set CREWAI_TRACING_ENABLED=true in your project's .env file -• Run: crewai traces enable""" - else: - message = """Info: Tracing is disabled. + message = """Info: Tracing is disabled. To enable tracing, do any one of these: • Set tracing=True in your Flow code diff --git a/lib/crewai/tests/tracing/test_tracing_message_suppression.py b/lib/crewai/tests/tracing/test_tracing_message_suppression.py new file mode 100644 index 000000000..1c226e27d --- /dev/null +++ b/lib/crewai/tests/tracing/test_tracing_message_suppression.py @@ -0,0 +1,259 @@ +"""Tests for tracing disabled message suppression (issue #5665). + +Verifies that: +- Users who explicitly declined tracing are NOT nagged with the message. +- The CREWAI_SUPPRESS_TRACING_MESSAGES env var suppresses the message. +- The message is shown only when tracing is disabled and user hasn't declined. +""" + +from unittest.mock import MagicMock, patch + +import pytest + +from crewai.events.listeners.tracing.utils import ( + set_suppress_tracing_messages, + should_suppress_tracing_messages, +) + + +class TestShouldSuppressTracingMessages: + """Tests for the should_suppress_tracing_messages utility function.""" + + def test_suppress_false_by_default(self): + """By default, messages should NOT be suppressed.""" + token = set_suppress_tracing_messages(False) + try: + assert should_suppress_tracing_messages() is False + finally: + from crewai.events.listeners.tracing.utils import ( + _suppress_tracing_messages, + ) + _suppress_tracing_messages.reset(token) + + def test_suppress_via_context_var(self): + """Setting the context var should suppress messages.""" + token = set_suppress_tracing_messages(True) + try: + assert should_suppress_tracing_messages() is True + finally: + from crewai.events.listeners.tracing.utils import ( + _suppress_tracing_messages, + ) + _suppress_tracing_messages.reset(token) + + @pytest.mark.parametrize("env_value", ["true", "True", "TRUE", "1", "yes", "YES"]) + def test_suppress_via_env_var(self, env_value, monkeypatch): + """CREWAI_SUPPRESS_TRACING_MESSAGES env var should suppress messages.""" + token = set_suppress_tracing_messages(False) + try: + monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", env_value) + assert should_suppress_tracing_messages() is True + finally: + from crewai.events.listeners.tracing.utils import ( + _suppress_tracing_messages, + ) + _suppress_tracing_messages.reset(token) + + @pytest.mark.parametrize("env_value", ["false", "False", "0", "no", ""]) + def test_no_suppress_with_falsy_env_var(self, env_value, monkeypatch): + """Falsy values for the env var should NOT suppress messages.""" + token = set_suppress_tracing_messages(False) + try: + monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", env_value) + assert should_suppress_tracing_messages() is False + finally: + from crewai.events.listeners.tracing.utils import ( + _suppress_tracing_messages, + ) + _suppress_tracing_messages.reset(token) + + def test_context_var_takes_precedence_over_env(self, monkeypatch): + """Context var set to True should suppress even if env var is false.""" + token = set_suppress_tracing_messages(True) + try: + monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "false") + assert should_suppress_tracing_messages() is True + finally: + from crewai.events.listeners.tracing.utils import ( + _suppress_tracing_messages, + ) + _suppress_tracing_messages.reset(token) + + +class TestShowTracingDisabledMessage: + """Tests that _show_tracing_disabled_message does not nag declined users.""" + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={"first_execution_done": True, "trace_consent": False}, + ) + def test_crew_no_message_when_user_declined(self, mock_load): + """Crew._show_tracing_disabled_message should not print when user declined.""" + from crewai.crew import Crew + + with patch("crewai.crew.Console") as MockConsole: + Crew._show_tracing_disabled_message() + MockConsole.return_value.print.assert_not_called() + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={"first_execution_done": True, "trace_consent": False}, + ) + def test_flow_no_message_when_user_declined(self, mock_load): + """Flow._show_tracing_disabled_message should not print when user declined.""" + from crewai.flow.flow import Flow + + with patch("crewai.flow.flow.Console") as MockConsole: + Flow._show_tracing_disabled_message() + MockConsole.return_value.print.assert_not_called() + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={"first_execution_done": True, "trace_consent": False}, + ) + def test_trace_listener_no_message_when_user_declined(self, mock_load): + """TraceCollectionListener._show_tracing_disabled_message should not print when user declined.""" + from crewai.events.listeners.tracing.trace_listener import ( + TraceCollectionListener, + ) + + listener = TraceCollectionListener.__new__(TraceCollectionListener) + with patch("rich.console.Console") as MockConsole: + listener._show_tracing_disabled_message() + MockConsole.return_value.print.assert_not_called() + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={}, + ) + def test_crew_shows_message_when_user_has_not_decided(self, mock_load): + """Crew._show_tracing_disabled_message should print when user hasn't decided yet.""" + from crewai.crew import Crew + + with patch("crewai.crew.Console") as MockConsole: + mock_console_instance = MockConsole.return_value + Crew._show_tracing_disabled_message() + mock_console_instance.print.assert_called_once() + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={}, + ) + def test_crew_no_message_when_suppress_env_set(self, mock_load, monkeypatch): + """Crew._show_tracing_disabled_message should not print when env var suppresses.""" + from crewai.crew import Crew + + monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "true") + with patch("crewai.crew.Console") as MockConsole: + Crew._show_tracing_disabled_message() + MockConsole.return_value.print.assert_not_called() + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={}, + ) + def test_flow_no_message_when_suppress_env_set(self, mock_load, monkeypatch): + """Flow._show_tracing_disabled_message should not print when env var suppresses.""" + from crewai.flow.flow import Flow + + monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "true") + with patch("crewai.flow.flow.Console") as MockConsole: + Flow._show_tracing_disabled_message() + MockConsole.return_value.print.assert_not_called() + + +class TestConsoleFormatterTracingMessage: + """Tests for console_formatter._show_tracing_disabled_message_if_needed.""" + + def _make_formatter(self): + from crewai.events.utils.console_formatter import ConsoleFormatter + + formatter = ConsoleFormatter.__new__(ConsoleFormatter) + formatter.console = MagicMock() + formatter.verbose = True + return formatter + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={"first_execution_done": True, "trace_consent": False}, + ) + def test_no_message_when_user_declined(self, mock_load): + """Console formatter should not show the message when user declined tracing.""" + formatter = self._make_formatter() + + with patch( + "crewai.events.listeners.tracing.trace_listener.TraceCollectionListener" + ) as mock_listener_cls: + mock_listener_cls._instance = None + formatter._show_tracing_disabled_message_if_needed() + + formatter.console.print.assert_not_called() + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={}, + ) + def test_no_message_when_suppress_env_set(self, mock_load, monkeypatch): + """Console formatter should not show the message when env var is set.""" + monkeypatch.setenv("CREWAI_SUPPRESS_TRACING_MESSAGES", "true") + formatter = self._make_formatter() + + formatter._show_tracing_disabled_message_if_needed() + + formatter.console.print.assert_not_called() + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={}, + ) + @patch( + "crewai.events.listeners.tracing.utils.is_tracing_enabled_in_context", + return_value=False, + ) + def test_message_shown_when_tracing_disabled_and_not_declined( + self, mock_tracing_ctx, mock_load + ): + """Console formatter should show the message when tracing disabled and user hasn't declined.""" + from crewai.events.listeners.tracing.trace_listener import ( + TraceCollectionListener, + ) + + formatter = self._make_formatter() + + mock_instance = MagicMock() + mock_instance.first_time_handler.is_first_time = False + original_instance = TraceCollectionListener._instance + + try: + TraceCollectionListener._instance = mock_instance # type: ignore[misc] + formatter._show_tracing_disabled_message_if_needed() + formatter.console.print.assert_called_once() + finally: + TraceCollectionListener._instance = original_instance # type: ignore[misc] + + @patch( + "crewai.events.listeners.tracing.utils._load_user_data", + return_value={}, + ) + @patch( + "crewai.events.listeners.tracing.utils.is_tracing_enabled_in_context", + return_value=True, + ) + def test_no_message_when_tracing_enabled(self, mock_tracing_ctx, mock_load): + """Console formatter should not show the message when tracing is enabled.""" + from crewai.events.listeners.tracing.trace_listener import ( + TraceCollectionListener, + ) + + formatter = self._make_formatter() + + mock_instance = MagicMock() + mock_instance.first_time_handler.is_first_time = False + original_instance = TraceCollectionListener._instance + + try: + TraceCollectionListener._instance = mock_instance # type: ignore[misc] + formatter._show_tracing_disabled_message_if_needed() + formatter.console.print.assert_not_called() + finally: + TraceCollectionListener._instance = original_instance # type: ignore[misc]