diff --git a/lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py b/lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py index ad56807e4..a44b81fc3 100644 --- a/lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py +++ b/lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py @@ -6,7 +6,6 @@ from pydantic import BaseModel, Field, PrivateAttr from crewai.agents.parser import AgentFinish from crewai.memory.utils import sanitize_scope_name -from crewai.utilities.printer import Printer from crewai.utilities.string_utils import sanitize_tool_name from crewai.utilities.types import LLMMessage @@ -30,7 +29,6 @@ class BaseAgentExecutor(BaseModel): messages: list[LLMMessage] = Field(default_factory=list) _resuming: bool = PrivateAttr(default=False) _i18n: I18N | None = PrivateAttr(default=None) - _printer: Printer = PrivateAttr(default_factory=Printer) def _save_to_memory(self, output: AgentFinish) -> None: """Save task result to unified memory (memory or crew._memory).""" diff --git a/lib/crewai/src/crewai/agents/crew_agent_executor.py b/lib/crewai/src/crewai/agents/crew_agent_executor.py index 0a002ed8e..6307d5b9c 100644 --- a/lib/crewai/src/crewai/agents/crew_agent_executor.py +++ b/lib/crewai/src/crewai/agents/crew_agent_executor.py @@ -68,6 +68,7 @@ from crewai.utilities.agent_utils import ( from crewai.utilities.constants import TRAINING_DATA_FILE from crewai.utilities.file_store import aget_all_files, get_all_files from crewai.utilities.i18n import I18N, get_i18n +from crewai.utilities.printer import PRINTER from crewai.utilities.string_utils import sanitize_tool_name from crewai.utilities.token_counter_callback import TokenCalcHandler from crewai.utilities.tool_utils import ( @@ -212,13 +213,13 @@ class CrewAgentExecutor(BaseAgentExecutor): formatted_answer = self._invoke_loop() except AssertionError: if self.agent.verbose: - self._printer.print( + PRINTER.print( content="Agent failed to reach a final answer. This is likely a bug - please report it.", color="red", ) raise except Exception as e: - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise if self.ask_for_human_input: @@ -326,7 +327,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if has_reached_max_iterations(self.iterations, self.max_iter): formatted_answer = handle_max_iterations_exceeded( formatted_answer, - printer=self._printer, + printer=PRINTER, i18n=self._i18n, messages=self.messages, llm=cast("BaseLLM", self.llm), @@ -341,7 +342,7 @@ class CrewAgentExecutor(BaseAgentExecutor): llm=cast("BaseLLM", self.llm), messages=self.messages, callbacks=self.callbacks, - printer=self._printer, + printer=PRINTER, from_task=self.task, from_agent=self.agent, response_model=self.response_model, @@ -422,7 +423,7 @@ class CrewAgentExecutor(BaseAgentExecutor): messages=self.messages, iterations=self.iterations, log_error_after=self.log_error_after, - printer=self._printer, + printer=PRINTER, verbose=self.agent.verbose, ) @@ -433,7 +434,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if is_context_length_exceeded(e): handle_context_length( respect_context_window=self.respect_context_window, - printer=self._printer, + printer=PRINTER, messages=self.messages, llm=cast("BaseLLM", self.llm), callbacks=self.callbacks, @@ -441,7 +442,7 @@ class CrewAgentExecutor(BaseAgentExecutor): verbose=self.agent.verbose, ) continue - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise e finally: self.iterations += 1 @@ -482,7 +483,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if has_reached_max_iterations(self.iterations, self.max_iter): formatted_answer = handle_max_iterations_exceeded( None, - printer=self._printer, + printer=PRINTER, i18n=self._i18n, messages=self.messages, llm=cast("BaseLLM", self.llm), @@ -502,7 +503,7 @@ class CrewAgentExecutor(BaseAgentExecutor): llm=cast("BaseLLM", self.llm), messages=self.messages, callbacks=self.callbacks, - printer=self._printer, + printer=PRINTER, tools=openai_tools, available_functions=None, from_task=self.task, @@ -570,7 +571,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if is_context_length_exceeded(e): handle_context_length( respect_context_window=self.respect_context_window, - printer=self._printer, + printer=PRINTER, messages=self.messages, llm=cast("BaseLLM", self.llm), callbacks=self.callbacks, @@ -578,7 +579,7 @@ class CrewAgentExecutor(BaseAgentExecutor): verbose=self.agent.verbose, ) continue - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise e finally: self.iterations += 1 @@ -595,7 +596,7 @@ class CrewAgentExecutor(BaseAgentExecutor): llm=cast("BaseLLM", self.llm), messages=self.messages, callbacks=self.callbacks, - printer=self._printer, + printer=PRINTER, from_task=self.task, from_agent=self.agent, response_model=self.response_model, @@ -965,7 +966,7 @@ class CrewAgentExecutor(BaseAgentExecutor): break except Exception as hook_error: if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Error in before_tool_call hook: {hook_error}", color="red", ) @@ -1031,7 +1032,7 @@ class CrewAgentExecutor(BaseAgentExecutor): after_hook_context.tool_result = result except Exception as hook_error: if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Error in after_tool_call hook: {hook_error}", color="red", ) @@ -1078,7 +1079,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if self.agent and self.agent.verbose: cache_info = " (from cache)" if from_cache else "" - self._printer.print( + PRINTER.print( content=f"Tool {func_name} executed with result{cache_info}: {result[:200]}...", color="green", ) @@ -1118,13 +1119,13 @@ class CrewAgentExecutor(BaseAgentExecutor): formatted_answer = await self._ainvoke_loop() except AssertionError: if self.agent.verbose: - self._printer.print( + PRINTER.print( content="Agent failed to reach a final answer. This is likely a bug - please report it.", color="red", ) raise except Exception as e: - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise if self.ask_for_human_input: @@ -1168,7 +1169,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if has_reached_max_iterations(self.iterations, self.max_iter): formatted_answer = handle_max_iterations_exceeded( formatted_answer, - printer=self._printer, + printer=PRINTER, i18n=self._i18n, messages=self.messages, llm=cast("BaseLLM", self.llm), @@ -1183,7 +1184,7 @@ class CrewAgentExecutor(BaseAgentExecutor): llm=cast("BaseLLM", self.llm), messages=self.messages, callbacks=self.callbacks, - printer=self._printer, + printer=PRINTER, from_task=self.task, from_agent=self.agent, response_model=self.response_model, @@ -1263,7 +1264,7 @@ class CrewAgentExecutor(BaseAgentExecutor): messages=self.messages, iterations=self.iterations, log_error_after=self.log_error_after, - printer=self._printer, + printer=PRINTER, verbose=self.agent.verbose, ) @@ -1273,7 +1274,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if is_context_length_exceeded(e): handle_context_length( respect_context_window=self.respect_context_window, - printer=self._printer, + printer=PRINTER, messages=self.messages, llm=cast("BaseLLM", self.llm), callbacks=self.callbacks, @@ -1281,7 +1282,7 @@ class CrewAgentExecutor(BaseAgentExecutor): verbose=self.agent.verbose, ) continue - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise e finally: self.iterations += 1 @@ -1316,7 +1317,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if has_reached_max_iterations(self.iterations, self.max_iter): formatted_answer = handle_max_iterations_exceeded( None, - printer=self._printer, + printer=PRINTER, i18n=self._i18n, messages=self.messages, llm=cast("BaseLLM", self.llm), @@ -1336,7 +1337,7 @@ class CrewAgentExecutor(BaseAgentExecutor): llm=cast("BaseLLM", self.llm), messages=self.messages, callbacks=self.callbacks, - printer=self._printer, + printer=PRINTER, tools=openai_tools, available_functions=None, from_task=self.task, @@ -1403,7 +1404,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if is_context_length_exceeded(e): handle_context_length( respect_context_window=self.respect_context_window, - printer=self._printer, + printer=PRINTER, messages=self.messages, llm=cast("BaseLLM", self.llm), callbacks=self.callbacks, @@ -1411,7 +1412,7 @@ class CrewAgentExecutor(BaseAgentExecutor): verbose=self.agent.verbose, ) continue - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise e finally: self.iterations += 1 @@ -1428,7 +1429,7 @@ class CrewAgentExecutor(BaseAgentExecutor): llm=cast("BaseLLM", self.llm), messages=self.messages, callbacks=self.callbacks, - printer=self._printer, + printer=PRINTER, from_task=self.task, from_agent=self.agent, response_model=self.response_model, @@ -1576,7 +1577,7 @@ class CrewAgentExecutor(BaseAgentExecutor): if train_iteration is None or not isinstance(train_iteration, int): if self.agent.verbose: - self._printer.print( + PRINTER.print( content="Invalid or missing train iteration. Cannot save training data.", color="red", ) @@ -1600,7 +1601,7 @@ class CrewAgentExecutor(BaseAgentExecutor): agent_training_data[train_iteration]["improved_output"] = result.output else: if self.agent.verbose: - self._printer.print( + PRINTER.print( content=( f"No existing training data for agent {agent_id} and iteration " f"{train_iteration}. Cannot save improved output." diff --git a/lib/crewai/src/crewai/agents/step_executor.py b/lib/crewai/src/crewai/agents/step_executor.py index 29836497c..48592efb4 100644 --- a/lib/crewai/src/crewai/agents/step_executor.py +++ b/lib/crewai/src/crewai/agents/step_executor.py @@ -40,7 +40,7 @@ from crewai.utilities.agent_utils import ( ) from crewai.utilities.i18n import I18N, get_i18n from crewai.utilities.planning_types import TodoItem -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER from crewai.utilities.step_execution_context import StepExecutionContext, StepResult from crewai.utilities.string_utils import sanitize_tool_name from crewai.utilities.tool_utils import execute_tool_and_check_finality @@ -109,7 +109,6 @@ class StepExecutor: self.request_within_rpm_limit = request_within_rpm_limit self.callbacks = callbacks or [] self._i18n: I18N = i18n or get_i18n() - self._printer: Printer = Printer() # Native tool support — set up once self._use_native_tools = check_native_tool_support( @@ -585,7 +584,7 @@ class StepExecutor: task=self.task, crew=self.crew, event_source=self, - printer=self._printer, + printer=PRINTER, verbose=bool(self.agent and self.agent.verbose), ) diff --git a/lib/crewai/src/crewai/cli/add_crew_to_flow.py b/lib/crewai/src/crewai/cli/add_crew_to_flow.py index a3e0f5209..c286b5010 100644 --- a/lib/crewai/src/crewai/cli/add_crew_to_flow.py +++ b/lib/crewai/src/crewai/cli/add_crew_to_flow.py @@ -3,17 +3,14 @@ from pathlib import Path import click from crewai.cli.utils import copy_template -from crewai.utilities.printer import Printer - - -_printer = Printer() +from crewai.utilities.printer import PRINTER def add_crew_to_flow(crew_name: str) -> None: """Add a new crew to the current flow.""" # Check if pyproject.toml exists in the current directory if not Path("pyproject.toml").exists(): - _printer.print( + PRINTER.print( "This command must be run from the root of a flow project.", color="red" ) raise click.ClickException( @@ -25,7 +22,7 @@ def add_crew_to_flow(crew_name: str) -> None: crews_folder = flow_folder / "src" / flow_folder.name / "crews" if not crews_folder.exists(): - _printer.print("Crews folder does not exist in the current flow.", color="red") + PRINTER.print("Crews folder does not exist in the current flow.", color="red") raise click.ClickException("Crews folder does not exist in the current flow.") # Create the crew within the flow's crews directory diff --git a/lib/crewai/src/crewai/cli/crew_chat.py b/lib/crewai/src/crewai/cli/crew_chat.py index bbbd51c0c..ad1c65894 100644 --- a/lib/crewai/src/crewai/cli/crew_chat.py +++ b/lib/crewai/src/crewai/cli/crew_chat.py @@ -19,12 +19,10 @@ from crewai.llm import LLM from crewai.llms.base_llm import BaseLLM from crewai.types.crew_chat import ChatInputField, ChatInputs from crewai.utilities.llm_utils import create_llm -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER from crewai.utilities.types import LLMMessage -_printer = Printer() - MIN_REQUIRED_VERSION: Final[Literal["0.98.0"]] = "0.98.0" @@ -121,9 +119,9 @@ def run_chat() -> None: def show_loading(event: threading.Event) -> None: """Display animated loading dots while processing.""" while not event.is_set(): - _printer.print(".", end="") + PRINTER.print(".", end="") time.sleep(1) - _printer.print("") + PRINTER.print("") def initialize_chat_llm(crew: Crew) -> LLM | BaseLLM | None: diff --git a/lib/crewai/src/crewai/experimental/agent_executor.py b/lib/crewai/src/crewai/experimental/agent_executor.py index 067489c8e..72b732766 100644 --- a/lib/crewai/src/crewai/experimental/agent_executor.py +++ b/lib/crewai/src/crewai/experimental/agent_executor.py @@ -98,7 +98,7 @@ from crewai.utilities.planning_types import ( TodoItem, TodoList, ) -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER from crewai.utilities.step_execution_context import StepExecutionContext, StepResult from crewai.utilities.string_utils import sanitize_tool_name from crewai.utilities.tool_utils import execute_tool_and_check_finality @@ -199,7 +199,6 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor ) _i18n: I18N = PrivateAttr(default_factory=get_i18n) - _printer: Printer = PrivateAttr(default_factory=Printer) _console: Console = PrivateAttr(default_factory=Console) _last_parser_error: OutputParserError | None = PrivateAttr(default=None) _last_context_error: Exception | None = PrivateAttr(default=None) @@ -503,7 +502,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content=( f"[Observe] Step {current_todo.step_number} " f"(effort={effort}): " @@ -553,7 +552,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor current_todo.step_number, result=current_todo.result ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content=( f"[Low] Step {current_todo.step_number} hard-failed " f"— triggering replan: {observation.replan_reason}" @@ -572,7 +571,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.agent.verbose: completed = self.state.todos.completed_count total = len(self.state.todos.items) - self._printer.print( + PRINTER.print( content=f"[Low] Step {current_todo.step_number} done ({completed}/{total}) — continuing", color="green", ) @@ -605,7 +604,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.agent.verbose: completed = self.state.todos.completed_count total = len(self.state.todos.items) - self._printer.print( + PRINTER.print( content=f"[Medium] Step {current_todo.step_number} succeeded ({completed}/{total}) — continuing", color="green", ) @@ -618,7 +617,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor current_todo.step_number, result=current_todo.result ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content=( f"[Medium] Step {current_todo.step_number} failed + replan required " f"— triggering replan: {observation.replan_reason}" @@ -638,7 +637,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.agent.verbose: failed = len(self.state.todos.get_failed_todos()) total = len(self.state.todos.items) - self._printer.print( + PRINTER.print( content=( f"[Medium] Step {current_todo.step_number} failed but no replan needed " f"({failed} failed/{total} total) — continuing" @@ -680,7 +679,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor current_todo.step_number, result=current_todo.result ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content="[Decide] Goal achieved early — finalizing", color="green", ) @@ -692,7 +691,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor current_todo.step_number, result=current_todo.result ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"[Decide] Full replan needed: {observation.replan_reason}", color="yellow", ) @@ -705,7 +704,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor current_todo.step_number, result=current_todo.result ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content="[Decide] Step failed — triggering replan", color="yellow", ) @@ -718,7 +717,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor current_todo.step_number, result=current_todo.result ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content="[Decide] Plan valid but refining upcoming steps", color="cyan", ) @@ -731,7 +730,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.agent.verbose: completed = self.state.todos.completed_count total = len(self.state.todos.items) - self._printer.print( + PRINTER.print( content=f"[Decide] Continue plan ({completed}/{total} done)", color="green", ) @@ -776,7 +775,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"[Refine] Updated {len(remaining)} pending step(s)", color="cyan", ) @@ -811,7 +810,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor ) if self.agent.verbose: - self._printer.print( + PRINTER.print( content="Goal achieved early — skipping remaining steps", color="green", ) @@ -829,7 +828,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.state.replan_count >= max_replans: if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Max replans ({max_replans}) reached — finalizing with current results", color="yellow", ) @@ -936,7 +935,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor # Plan-and-Execute path: use StepExecutor for isolated execution if getattr(self.agent, "planning_enabled", False): if self.agent.verbose: - self._printer.print( + PRINTER.print( content=( f"[Execute] Step {current.step_number}: " f"{current.description[:60]}..." @@ -971,7 +970,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.agent.verbose: status = "success" if result.success else "failed" - self._printer.print( + PRINTER.print( content=( f"[Execute] Step {current.step_number} {status} " f"({result.execution_time:.1f}s, " @@ -1080,7 +1079,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor todo.result = error_msg self.state.todos.mark_failed(todo.step_number, result=error_msg) if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Todo {todo.step_number} failed: {error_msg}", color="red", ) @@ -1105,7 +1104,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.agent.verbose: status = "success" if step_result.success else "failed" - self._printer.print( + PRINTER.print( content=( f"[Execute] Step {todo.step_number} {status} " f"({step_result.execution_time:.1f}s, " @@ -1152,7 +1151,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor self.state.todos.mark_failed(todo.step_number, result=todo.result) if self.agent.verbose: - self._printer.print( + PRINTER.print( content=( f"[Observe] Step {todo.step_number} " f"(effort={effort}): " @@ -1203,7 +1202,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor """Force agent to provide final answer when max iterations exceeded.""" formatted_answer = handle_max_iterations_exceeded( formatted_answer=None, - printer=self._printer, + printer=PRINTER, i18n=self._i18n, messages=list(self.state.messages), llm=self.llm, @@ -1232,7 +1231,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor llm=self.llm, messages=list(self.state.messages), callbacks=self.callbacks, - printer=self._printer, + printer=PRINTER, from_task=self.task, from_agent=self.agent, response_model=self.response_model, @@ -1282,7 +1281,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor return "context_error" if e.__class__.__module__.startswith("litellm"): raise e - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise @router("continue_reasoning_native") @@ -1318,7 +1317,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor llm=self.llm, messages=list(self.state.messages), callbacks=self.callbacks, - printer=self._printer, + printer=PRINTER, tools=self._openai_tools, available_functions=None, from_task=self.task, @@ -1373,7 +1372,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor return "context_error" if e.__class__.__module__.startswith("litellm"): raise e - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise def _route_finish_with_todos( @@ -1442,9 +1441,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor ) except Exception as e: if self.agent and self.agent.verbose: - self._printer.print( - content=f"Error in tool execution: {e}", color="red" - ) + PRINTER.print(content=f"Error in tool execution: {e}", color="red") if self.task: self.task.increment_tools_errors() @@ -1598,7 +1595,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor # Log the tool execution if self.agent and self.agent.verbose: cache_info = " (from cache)" if from_cache else "" - self._printer.print( + PRINTER.print( content=f"Tool {func_name} executed with result{cache_info}: {result[:200]}...", color="green", ) @@ -1636,7 +1633,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor # Log the tool execution if self.agent and self.agent.verbose: cache_info = " (from cache)" if from_cache else "" - self._printer.print( + PRINTER.print( content=f"Tool {func_name} executed with result{cache_info}: {result[:200]}...", color="green", ) @@ -1800,7 +1797,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor break except Exception as hook_error: if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Error in before_tool_call hook: {hook_error}", color="red", ) @@ -1875,7 +1872,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor after_hook_context.tool_result = result except Exception as hook_error: if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Error in after_tool_call hook: {hook_error}", color="red", ) @@ -2033,7 +2030,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.agent.verbose: completed = self.state.todos.completed_count total = len(self.state.todos.items) - self._printer.print( + PRINTER.print( content=f"✓ Todo {step_number} completed ({completed}/{total})", color="green", ) @@ -2100,7 +2097,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor self._finalize_called = True if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"[Finalize] todos_count={len(self.state.todos.items)}, todos_with_results={sum(1 for t in self.state.todos.items if t.result)}", color="magenta", ) @@ -2263,7 +2260,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor except Exception as e: if self.agent and self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Synthesis LLM call failed ({e}), falling back to concatenation", color="yellow", ) @@ -2348,7 +2345,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor self.state.last_replan_reason = reason if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Triggering replan (attempt {self.state.replan_count}): {reason}", color="yellow", ) @@ -2408,7 +2405,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor self.state.todos.replace_pending_todos(new_todos) if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Replan: {len(new_todos)} new steps (completed history preserved)", color="green", ) @@ -2492,7 +2489,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor if self.state.replan_count >= max_replans: if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Max replans ({max_replans}) reached — finalizing with current results", color="yellow", ) @@ -2518,7 +2515,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor messages=list(self.state.messages), iterations=self.state.iterations, log_error_after=self.log_error_after, - printer=self._printer, + printer=PRINTER, verbose=self.agent.verbose, ) @@ -2534,7 +2531,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor """Recover from context length errors and retry.""" handle_context_length( respect_context_window=self.respect_context_window, - printer=self._printer, + printer=PRINTER, messages=self.state.messages, llm=self.llm, callbacks=self.callbacks, @@ -2637,7 +2634,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor self._console.print(fail_text) raise except Exception as e: - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise finally: self._is_executing = False @@ -2728,7 +2725,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor self._console.print(fail_text) raise except Exception as e: - handle_unknown_error(self._printer, e, verbose=self.agent.verbose) + handle_unknown_error(PRINTER, e, verbose=self.agent.verbose) raise finally: self._is_executing = False @@ -2793,7 +2790,7 @@ class AgentExecutor(Flow[AgentExecutorState], BaseAgentExecutor): # type: ignor task.result() except Exception as e: if self.agent.verbose: - self._printer.print( + PRINTER.print( content=f"Error in async step_callback task: {e!s}", color="red", ) diff --git a/lib/crewai/src/crewai/flow/persistence/decorators.py b/lib/crewai/src/crewai/flow/persistence/decorators.py index 20c860353..937b557f4 100644 --- a/lib/crewai/src/crewai/flow/persistence/decorators.py +++ b/lib/crewai/src/crewai/flow/persistence/decorators.py @@ -28,13 +28,13 @@ import asyncio from collections.abc import Callable import functools import logging -from typing import TYPE_CHECKING, Any, ClassVar, Final, TypeVar, cast +from typing import TYPE_CHECKING, Any, Final, TypeVar, cast from pydantic import BaseModel from crewai.flow.persistence.base import FlowPersistence from crewai.flow.persistence.sqlite import SQLiteFlowPersistence -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER if TYPE_CHECKING: @@ -56,8 +56,6 @@ LOG_MESSAGES: Final[dict[str, str]] = { class PersistenceDecorator: """Class to handle flow state persistence with consistent logging.""" - _printer: ClassVar[Printer] = Printer() - @classmethod def persist_state( cls, @@ -104,7 +102,7 @@ class PersistenceDecorator: # Log state saving only if verbose is True if verbose: - cls._printer.print( + PRINTER.print( LOG_MESSAGES["save_state"].format(flow_uuid), color="cyan" ) logger.info(LOG_MESSAGES["save_state"].format(flow_uuid)) @@ -119,19 +117,19 @@ class PersistenceDecorator: except Exception as e: error_msg = LOG_MESSAGES["save_error"].format(method_name, str(e)) if verbose: - cls._printer.print(error_msg, color="red") + PRINTER.print(error_msg, color="red") logger.error(error_msg) raise RuntimeError(f"State persistence failed: {e!s}") from e except AttributeError as e: error_msg = LOG_MESSAGES["state_missing"] if verbose: - cls._printer.print(error_msg, color="red") + PRINTER.print(error_msg, color="red") logger.error(error_msg) raise ValueError(error_msg) from e except (TypeError, ValueError) as e: error_msg = LOG_MESSAGES["id_missing"] if verbose: - cls._printer.print(error_msg, color="red") + PRINTER.print(error_msg, color="red") logger.error(error_msg) raise ValueError(error_msg) from e diff --git a/lib/crewai/src/crewai/flow/utils.py b/lib/crewai/src/crewai/flow/utils.py index 5dc812fc3..652a38f4c 100644 --- a/lib/crewai/src/crewai/flow/utils.py +++ b/lib/crewai/src/crewai/flow/utils.py @@ -32,14 +32,12 @@ from crewai.flow.flow_wrappers import ( SimpleFlowCondition, ) from crewai.flow.types import FlowMethodCallable, FlowMethodName -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER if TYPE_CHECKING: from crewai.flow.flow import Flow -_printer = Printer() - def _extract_string_literals_from_type_annotation( node: ast.expr, @@ -181,7 +179,7 @@ def get_possible_return_constants( return None except Exception as e: if verbose: - _printer.print( + PRINTER.print( f"Error retrieving source code for function {function.__name__}: {e}", color="red", ) @@ -194,27 +192,27 @@ def get_possible_return_constants( code_ast = ast.parse(source) except IndentationError as e: if verbose: - _printer.print( + PRINTER.print( f"IndentationError while parsing source code of {function.__name__}: {e}", color="red", ) - _printer.print(f"Source code:\n{source}", color="yellow") + PRINTER.print(f"Source code:\n{source}", color="yellow") return None except SyntaxError as e: if verbose: - _printer.print( + PRINTER.print( f"SyntaxError while parsing source code of {function.__name__}: {e}", color="red", ) - _printer.print(f"Source code:\n{source}", color="yellow") + PRINTER.print(f"Source code:\n{source}", color="yellow") return None except Exception as e: if verbose: - _printer.print( + PRINTER.print( f"Unexpected error while parsing source code of {function.__name__}: {e}", color="red", ) - _printer.print(f"Source code:\n{source}", color="yellow") + PRINTER.print(f"Source code:\n{source}", color="yellow") return None return_values: set[str] = set() @@ -395,13 +393,13 @@ def get_possible_return_constants( StateAttributeVisitor().visit(class_ast) except Exception as e: if verbose: - _printer.print( + PRINTER.print( f"Could not analyze class context for {function.__name__}: {e}", color="yellow", ) except Exception as e: if verbose: - _printer.print( + PRINTER.print( f"Could not introspect class for {function.__name__}: {e}", color="yellow", ) diff --git a/lib/crewai/src/crewai/hooks/llm_hooks.py b/lib/crewai/src/crewai/hooks/llm_hooks.py index 3a6abbedf..bc3d1d17d 100644 --- a/lib/crewai/src/crewai/hooks/llm_hooks.py +++ b/lib/crewai/src/crewai/hooks/llm_hooks.py @@ -9,7 +9,7 @@ from crewai.hooks.types import ( BeforeLLMCallHookCallable, BeforeLLMCallHookType, ) -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER if TYPE_CHECKING: @@ -138,16 +138,15 @@ class LLMCallHookContext: ... print("LLM call skipped by user") """ - printer = Printer() event_listener.formatter.pause_live_updates() try: - printer.print(content=f"\n{prompt}", color="bold_yellow") - printer.print(content=default_message, color="cyan") + PRINTER.print(content=f"\n{prompt}", color="bold_yellow") + PRINTER.print(content=default_message, color="cyan") response = input().strip() if response: - printer.print(content="\nProcessing your input...", color="cyan") + PRINTER.print(content="\nProcessing your input...", color="cyan") return response finally: diff --git a/lib/crewai/src/crewai/hooks/tool_hooks.py b/lib/crewai/src/crewai/hooks/tool_hooks.py index ac7f5c362..6d9c015b5 100644 --- a/lib/crewai/src/crewai/hooks/tool_hooks.py +++ b/lib/crewai/src/crewai/hooks/tool_hooks.py @@ -9,7 +9,7 @@ from crewai.hooks.types import ( BeforeToolCallHookCallable, BeforeToolCallHookType, ) -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER if TYPE_CHECKING: @@ -100,16 +100,15 @@ class ToolCallHookContext: ... return None # Allow execution """ - printer = Printer() event_listener.formatter.pause_live_updates() try: - printer.print(content=f"\n{prompt}", color="bold_yellow") - printer.print(content=default_message, color="cyan") + PRINTER.print(content=f"\n{prompt}", color="bold_yellow") + PRINTER.print(content=default_message, color="cyan") response = input().strip() if response: - printer.print(content="\nProcessing your input...", color="cyan") + PRINTER.print(content="\nProcessing your input...", color="cyan") return response finally: diff --git a/lib/crewai/src/crewai/lite_agent.py b/lib/crewai/src/crewai/lite_agent.py index 2bed7e92f..f96c84493 100644 --- a/lib/crewai/src/crewai/lite_agent.py +++ b/lib/crewai/src/crewai/lite_agent.py @@ -91,7 +91,7 @@ from crewai.utilities.guardrail import process_guardrail from crewai.utilities.guardrail_types import GuardrailCallable, GuardrailType from crewai.utilities.i18n import I18N, get_i18n from crewai.utilities.llm_utils import create_llm -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER from crewai.utilities.pydantic_schema_utils import generate_model_description from crewai.utilities.token_counter_callback import TokenCalcHandler from crewai.utilities.tool_utils import execute_tool_and_check_finality @@ -270,7 +270,6 @@ class LiteAgent(FlowTrackable, BaseModel): _key: str = PrivateAttr(default_factory=lambda: str(uuid.uuid4())) _messages: list[LLMMessage] = PrivateAttr(default_factory=list) _iterations: int = PrivateAttr(default=0) - _printer: Printer = PrivateAttr(default_factory=Printer) _guardrail: GuardrailCallable | None = PrivateAttr(default=None) _guardrail_retry_count: int = PrivateAttr(default=0) _callbacks: list[TokenCalcHandler] = PrivateAttr(default_factory=list) @@ -528,11 +527,11 @@ class LiteAgent(FlowTrackable, BaseModel): except Exception as e: if self.verbose: - self._printer.print( + PRINTER.print( content="Agent failed to reach a final answer. This is likely a bug - please report it.", color="red", ) - handle_unknown_error(self._printer, e, verbose=self.verbose) + handle_unknown_error(PRINTER, e, verbose=self.verbose) # Emit error event crewai_event_bus.emit( self, @@ -609,7 +608,7 @@ class LiteAgent(FlowTrackable, BaseModel): self._memory.remember_many(extracted, agent_role=self.role) except Exception as e: if self.verbose: - self._printer.print( + PRINTER.print( content=f"Failed to save to memory: {e}", color="yellow", ) @@ -661,7 +660,7 @@ class LiteAgent(FlowTrackable, BaseModel): formatted_result = result except ConverterError as e: if self.verbose: - self._printer.print( + PRINTER.print( content=f"Failed to parse output into response format after retries: {e.message}", color="yellow", ) @@ -704,7 +703,7 @@ class LiteAgent(FlowTrackable, BaseModel): ) self._guardrail_retry_count += 1 if self.verbose: - self._printer.print( + PRINTER.print( f"Guardrail failed. Retrying ({self._guardrail_retry_count}/{self.guardrail_max_retries})..." f"\n{guardrail_result.error}" ) @@ -875,7 +874,7 @@ class LiteAgent(FlowTrackable, BaseModel): if has_reached_max_iterations(self._iterations, self.max_iterations): formatted_answer = handle_max_iterations_exceeded( formatted_answer, - printer=self._printer, + printer=PRINTER, i18n=self.i18n, messages=self._messages, llm=cast(LLM, self.llm), @@ -890,7 +889,7 @@ class LiteAgent(FlowTrackable, BaseModel): llm=cast(LLM, self.llm), messages=self._messages, callbacks=self._callbacks, - printer=self._printer, + printer=PRINTER, from_agent=self, # type: ignore[arg-type] executor_context=self, response_model=response_model, @@ -933,7 +932,7 @@ class LiteAgent(FlowTrackable, BaseModel): self._append_message(formatted_answer.text, role="assistant") except OutputParserError as e: if self.verbose: - self._printer.print( + PRINTER.print( content="Failed to parse LLM output. Retrying...", color="yellow", ) @@ -942,7 +941,7 @@ class LiteAgent(FlowTrackable, BaseModel): messages=self._messages, iterations=self._iterations, log_error_after=3, - printer=self._printer, + printer=PRINTER, verbose=self.verbose, ) @@ -953,7 +952,7 @@ class LiteAgent(FlowTrackable, BaseModel): if is_context_length_exceeded(e): handle_context_length( respect_context_window=self.respect_context_window, - printer=self._printer, + printer=PRINTER, messages=self._messages, llm=cast(LLM, self.llm), callbacks=self._callbacks, @@ -961,7 +960,7 @@ class LiteAgent(FlowTrackable, BaseModel): verbose=self.verbose, ) continue - handle_unknown_error(self._printer, e, verbose=self.verbose) + handle_unknown_error(PRINTER, e, verbose=self.verbose) raise e finally: diff --git a/lib/crewai/src/crewai/llms/base_llm.py b/lib/crewai/src/crewai/llms/base_llm.py index fd3c8c45e..41ce1d2cd 100644 --- a/lib/crewai/src/crewai/llms/base_llm.py +++ b/lib/crewai/src/crewai/llms/base_llm.py @@ -857,7 +857,7 @@ class BaseLLM(BaseModel, ABC): LLMCallHookContext, get_before_llm_call_hooks, ) - from crewai.utilities.printer import Printer + from crewai.utilities.printer import PRINTER before_hooks = get_before_llm_call_hooks() if not before_hooks: @@ -872,21 +872,20 @@ class BaseLLM(BaseModel, ABC): crew=None, ) verbose = getattr(from_agent, "verbose", True) if from_agent else True - printer = Printer() try: for hook in before_hooks: result = hook(hook_context) if result is False: if verbose: - printer.print( + PRINTER.print( content="LLM call blocked by before_llm_call hook", color="yellow", ) return False except Exception as e: if verbose: - printer.print( + PRINTER.print( content=f"Error in before_llm_call hook: {e}", color="yellow", ) @@ -927,7 +926,7 @@ class BaseLLM(BaseModel, ABC): LLMCallHookContext, get_after_llm_call_hooks, ) - from crewai.utilities.printer import Printer + from crewai.utilities.printer import PRINTER after_hooks = get_after_llm_call_hooks() if not after_hooks: @@ -943,7 +942,6 @@ class BaseLLM(BaseModel, ABC): response=response, ) verbose = getattr(from_agent, "verbose", True) if from_agent else True - printer = Printer() modified_response = response try: @@ -954,7 +952,7 @@ class BaseLLM(BaseModel, ABC): hook_context.response = modified_response except Exception as e: if verbose: - printer.print( + PRINTER.print( content=f"Error in after_llm_call hook: {e}", color="yellow", ) diff --git a/lib/crewai/src/crewai/memory/storage/kickoff_task_outputs_storage.py b/lib/crewai/src/crewai/memory/storage/kickoff_task_outputs_storage.py index 6cc6b6c64..3f5f38c9f 100644 --- a/lib/crewai/src/crewai/memory/storage/kickoff_task_outputs_storage.py +++ b/lib/crewai/src/crewai/memory/storage/kickoff_task_outputs_storage.py @@ -6,7 +6,6 @@ import sqlite3 from typing import Any from crewai.task import Task -from crewai.utilities import Printer from crewai.utilities.crew_json_encoder import CrewJSONEncoder from crewai.utilities.errors import DatabaseError, DatabaseOperationError from crewai.utilities.lock_store import lock as store_lock @@ -27,7 +26,6 @@ class KickoffTaskOutputsSQLiteStorage: db_path = str(Path(db_storage_path()) / "latest_kickoff_task_outputs.db") self.db_path = db_path self._lock_name = f"sqlite:{os.path.realpath(self.db_path)}" - self._printer: Printer = Printer() self._initialize_db() def _initialize_db(self) -> None: diff --git a/lib/crewai/src/crewai/rag/embeddings/providers/ibm/embedding_callable.py b/lib/crewai/src/crewai/rag/embeddings/providers/ibm/embedding_callable.py index 7104c1705..44e97149a 100644 --- a/lib/crewai/src/crewai/rag/embeddings/providers/ibm/embedding_callable.py +++ b/lib/crewai/src/crewai/rag/embeddings/providers/ibm/embedding_callable.py @@ -6,10 +6,7 @@ from chromadb.api.types import Documents, EmbeddingFunction, Embeddings from typing_extensions import Unpack from crewai.rag.embeddings.providers.ibm.types import WatsonXProviderConfig -from crewai.utilities.printer import Printer - - -_printer = Printer() +from crewai.utilities.printer import PRINTER class WatsonXEmbeddingFunction(EmbeddingFunction[Documents]): @@ -164,5 +161,5 @@ class WatsonXEmbeddingFunction(EmbeddingFunction[Documents]): return cast(Embeddings, embeddings) except Exception as e: if self._verbose: - _printer.print(f"Error during WatsonX embedding: {e}", color="red") + PRINTER.print(f"Error during WatsonX embedding: {e}", color="red") raise diff --git a/lib/crewai/src/crewai/task.py b/lib/crewai/src/crewai/task.py index 73e49ade9..5671282dc 100644 --- a/lib/crewai/src/crewai/task.py +++ b/lib/crewai/src/crewai/task.py @@ -81,13 +81,10 @@ from crewai.utilities.guardrail_types import ( GuardrailsType, ) from crewai.utilities.i18n import I18N, get_i18n -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER from crewai.utilities.string_utils import interpolate_only -_printer = Printer() - - class Task(BaseModel): """Class that represents a task to be executed. @@ -981,7 +978,7 @@ Follow these guidelines: crew_chat_messages = json.loads(crew_chat_messages_json) except json.JSONDecodeError as e: if self.agent and self.agent.verbose: - _printer.print( + PRINTER.print( f"An error occurred while parsing crew chat messages: {e}", color="red", ) @@ -1227,8 +1224,7 @@ Follow these guidelines: task_output=task_output.raw, ) if agent and agent.verbose: - printer = Printer() - printer.print( + PRINTER.print( content=f"Guardrail {guardrail_index if guardrail_index is not None else ''} blocked (attempt {attempt + 1}/{max_attempts}), retrying due to: {guardrail_result.error}\n", color="yellow", ) @@ -1325,8 +1321,7 @@ Follow these guidelines: task_output=task_output.raw, ) if agent and agent.verbose: - printer = Printer() - printer.print( + PRINTER.print( content=f"Guardrail {guardrail_index if guardrail_index is not None else ''} blocked (attempt {attempt + 1}/{max_attempts}), retrying due to: {guardrail_result.error}\n", color="yellow", ) diff --git a/lib/crewai/src/crewai/tools/base_tool.py b/lib/crewai/src/crewai/tools/base_tool.py index 11f88a768..e1dc8f2ee 100644 --- a/lib/crewai/src/crewai/tools/base_tool.py +++ b/lib/crewai/src/crewai/tools/base_tool.py @@ -38,13 +38,10 @@ from crewai.tools.structured_tool import ( build_schema_hint, ) from crewai.types.callback import SerializableCallable, _resolve_dotted_path -from crewai.utilities.printer import Printer from crewai.utilities.pydantic_schema_utils import generate_model_description from crewai.utilities.string_utils import sanitize_tool_name -_printer = Printer() - P = ParamSpec("P") R = TypeVar("R", covariant=True) diff --git a/lib/crewai/src/crewai/tools/tool_usage.py b/lib/crewai/src/crewai/tools/tool_usage.py index 95adc0906..c99b32cf5 100644 --- a/lib/crewai/src/crewai/tools/tool_usage.py +++ b/lib/crewai/src/crewai/tools/tool_usage.py @@ -29,7 +29,7 @@ from crewai.utilities.agent_utils import ( ) from crewai.utilities.converter import Converter from crewai.utilities.i18n import I18N, get_i18n -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER from crewai.utilities.string_utils import sanitize_tool_name @@ -94,7 +94,6 @@ class ToolUsage: fingerprint_context: dict[str, str] | None = None, ) -> None: self._i18n: I18N = agent.i18n if agent else get_i18n() - self._printer: Printer = Printer() self._telemetry: Telemetry = Telemetry() self._run_attempts: int = 1 self._max_parsing_attempts: int = 3 @@ -129,7 +128,7 @@ class ToolUsage: if isinstance(calling, ToolUsageError): error = calling.message if self.agent and self.agent.verbose: - self._printer.print(content=f"\n\n{error}\n", color="red") + PRINTER.print(content=f"\n\n{error}\n", color="red") if self.task: self.task.increment_tools_errors() return error @@ -141,7 +140,7 @@ class ToolUsage: if self.task: self.task.increment_tools_errors() if self.agent and self.agent.verbose: - self._printer.print(content=f"\n\n{error}\n", color="red") + PRINTER.print(content=f"\n\n{error}\n", color="red") return error if ( @@ -157,7 +156,7 @@ class ToolUsage: if self.task: self.task.increment_tools_errors() if self.agent and self.agent.verbose: - self._printer.print(content=f"\n\n{error}\n", color="red") + PRINTER.print(content=f"\n\n{error}\n", color="red") return error return f"{self._use(tool_string=tool_string, tool=tool, calling=calling)}" @@ -177,7 +176,7 @@ class ToolUsage: if isinstance(calling, ToolUsageError): error = calling.message if self.agent and self.agent.verbose: - self._printer.print(content=f"\n\n{error}\n", color="red") + PRINTER.print(content=f"\n\n{error}\n", color="red") if self.task: self.task.increment_tools_errors() return error @@ -189,7 +188,7 @@ class ToolUsage: if self.task: self.task.increment_tools_errors() if self.agent and self.agent.verbose: - self._printer.print(content=f"\n\n{error}\n", color="red") + PRINTER.print(content=f"\n\n{error}\n", color="red") return error if ( @@ -206,7 +205,7 @@ class ToolUsage: if self.task: self.task.increment_tools_errors() if self.agent and self.agent.verbose: - self._printer.print(content=f"\n\n{error}\n", color="red") + PRINTER.print(content=f"\n\n{error}\n", color="red") return error return ( @@ -391,7 +390,7 @@ class ToolUsage: and self.agent and self.agent.verbose ): - self._printer.print( + PRINTER.print( content=f"Tool '{sanitize_tool_name(available_tool.name)}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}", color="blue", ) @@ -405,7 +404,7 @@ class ToolUsage: and self.agent and self.agent.verbose ): - self._printer.print( + PRINTER.print( content=f"Tool '{sanitize_tool_name(available_tool.name)}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}", color="blue", ) @@ -429,9 +428,7 @@ class ToolUsage: if self.task: self.task.increment_tools_errors() if self.agent and self.agent.verbose: - self._printer.print( - content=f"\n\n{error_message}\n", color="red" - ) + PRINTER.print(content=f"\n\n{error_message}\n", color="red") else: if self.task: self.task.increment_tools_errors() @@ -626,7 +623,7 @@ class ToolUsage: and self.agent and self.agent.verbose ): - self._printer.print( + PRINTER.print( content=f"Tool '{sanitize_tool_name(available_tool.name)}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}", color="blue", ) @@ -640,7 +637,7 @@ class ToolUsage: and self.agent and self.agent.verbose ): - self._printer.print( + PRINTER.print( content=f"Tool '{sanitize_tool_name(available_tool.name)}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}", color="blue", ) @@ -664,9 +661,7 @@ class ToolUsage: if self.task: self.task.increment_tools_errors() if self.agent and self.agent.verbose: - self._printer.print( - content=f"\n\n{error_message}\n", color="red" - ) + PRINTER.print(content=f"\n\n{error_message}\n", color="red") else: if self.task: self.task.increment_tools_errors() @@ -859,7 +854,7 @@ class ToolUsage: if self.task: self.task.increment_tools_errors() if self.agent and self.agent.verbose: - self._printer.print(content=f"\n\n{e}\n", color="red") + PRINTER.print(content=f"\n\n{e}\n", color="red") return ToolUsageError( f"{self._i18n.errors('tool_usage_error').format(error=e)}\nMoving on then. {self._i18n.slice('format').format(tool_names=self.tools_names)}" ) @@ -903,16 +898,14 @@ class ToolUsage: try: repaired_input = str(repair_json(tool_input, skip_json_loads=True)) if self.agent and self.agent.verbose: - self._printer.print( - content=f"Repaired JSON: {repaired_input}", color="blue" - ) + PRINTER.print(content=f"Repaired JSON: {repaired_input}", color="blue") arguments = json.loads(repaired_input) if isinstance(arguments, dict): return arguments except Exception as e: error = f"Failed to repair JSON: {e}" if self.agent and self.agent.verbose: - self._printer.print(content=error, color="red") + PRINTER.print(content=error, color="red") error_message = ( "Tool input must be a valid dictionary in JSON or Python literal format" diff --git a/lib/crewai/src/crewai/utilities/agent_utils.py b/lib/crewai/src/crewai/utilities/agent_utils.py index 09c570fac..d448cd162 100644 --- a/lib/crewai/src/crewai/utilities/agent_utils.py +++ b/lib/crewai/src/crewai/utilities/agent_utils.py @@ -32,7 +32,7 @@ from crewai.utilities.exceptions.context_window_exceeding_exception import ( LLMContextLengthExceededError, ) from crewai.utilities.i18n import I18N -from crewai.utilities.printer import ColoredText, Printer +from crewai.utilities.printer import PRINTER, ColoredText, Printer from crewai.utilities.pydantic_schema_utils import generate_model_description from crewai.utilities.string_utils import sanitize_tool_name from crewai.utilities.token_counter_callback import TokenCalcHandler @@ -946,7 +946,7 @@ def summarize_messages( summarized_contents: list[SummaryContent] = [] for idx, chunk in enumerate(chunks, 1): if verbose: - Printer().print( + PRINTER.print( content=f"Summarizing {idx}/{total_chunks}...", color="yellow", ) @@ -967,7 +967,7 @@ def summarize_messages( else: # Multiple chunks — summarize in parallel via asyncio if verbose: - Printer().print( + PRINTER.print( content=f"Summarizing {total_chunks} chunks in parallel...", color="yellow", ) diff --git a/lib/crewai/src/crewai/utilities/converter.py b/lib/crewai/src/crewai/utilities/converter.py index 67f542d53..328ecbdf9 100644 --- a/lib/crewai/src/crewai/utilities/converter.py +++ b/lib/crewai/src/crewai/utilities/converter.py @@ -10,7 +10,7 @@ from typing_extensions import Unpack from crewai.agents.agent_builder.utilities.base_output_converter import OutputConverter from crewai.utilities.i18n import get_i18n from crewai.utilities.internal_instructor import InternalInstructor -from crewai.utilities.printer import Printer +from crewai.utilities.printer import PRINTER from crewai.utilities.pydantic_schema_utils import generate_model_description @@ -209,7 +209,7 @@ def convert_to_model( except Exception as e: if agent and getattr(agent, "verbose", True): - Printer().print( + PRINTER.print( content=f"Unexpected error during model conversion: {type(e).__name__}: {e}. Returning original result.", color="red", ) @@ -267,7 +267,7 @@ def handle_partial_json( raise except Exception as e: if agent and getattr(agent, "verbose", True): - Printer().print( + PRINTER.print( content=f"Unexpected error during partial JSON handling: {type(e).__name__}: {e}. Attempting alternative conversion method.", color="red", ) @@ -329,7 +329,7 @@ def convert_with_instructions( if isinstance(exported_result, ConverterError): if agent and getattr(agent, "verbose", True): - Printer().print( + PRINTER.print( content=f"Failed to convert result to model: {exported_result}", color="red", ) diff --git a/lib/crewai/src/crewai/utilities/logger.py b/lib/crewai/src/crewai/utilities/logger.py index 6796f26e0..afc09d693 100644 --- a/lib/crewai/src/crewai/utilities/logger.py +++ b/lib/crewai/src/crewai/utilities/logger.py @@ -1,8 +1,8 @@ from datetime import datetime -from pydantic import BaseModel, Field, PrivateAttr +from pydantic import BaseModel, Field -from crewai.utilities.printer import ColoredText, Printer, PrinterColor +from crewai.utilities.printer import PRINTER, ColoredText, PrinterColor class Logger(BaseModel): @@ -14,7 +14,6 @@ class Logger(BaseModel): default="bold_yellow", description="Default color for log messages", ) - _printer: Printer = PrivateAttr(default_factory=Printer) def log(self, level: str, message: str, color: PrinterColor | None = None) -> None: """Log a message with timestamp if verbose mode is enabled. @@ -26,7 +25,7 @@ class Logger(BaseModel): """ if self.verbose: timestamp: str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - self._printer.print( + PRINTER.print( [ ColoredText(f"\n[{timestamp}]", "cyan"), ColoredText(f"[{level.upper()}]: ", "yellow"), diff --git a/lib/crewai/src/crewai/utilities/printer.py b/lib/crewai/src/crewai/utilities/printer.py index 949da543a..bb0dfecba 100644 --- a/lib/crewai/src/crewai/utilities/printer.py +++ b/lib/crewai/src/crewai/utilities/printer.py @@ -93,3 +93,6 @@ class Printer: file=file, flush=flush, ) + + +PRINTER: Printer = Printer() diff --git a/lib/crewai/tests/agents/test_agent_executor.py b/lib/crewai/tests/agents/test_agent_executor.py index 91fa12f27..7a6260a44 100644 --- a/lib/crewai/tests/agents/test_agent_executor.py +++ b/lib/crewai/tests/agents/test_agent_executor.py @@ -48,8 +48,6 @@ def _build_executor(**kwargs: Any) -> AgentExecutor: executor._last_context_error = None executor._step_executor = None executor._planner_observer = None - from crewai.utilities.printer import Printer - executor._printer = Printer() from crewai.utilities.i18n import get_i18n executor._i18n = kwargs.get("i18n") or get_i18n() return executor @@ -1491,7 +1489,6 @@ class TestReasoningEffort: executor.handle_step_observed_medium = ( AgentExecutor.handle_step_observed_medium.__get__(executor) ) - executor._printer = Mock() # --- Case 1: step succeeded → should return "continue_plan" --- success_todo = TodoItem( @@ -1562,7 +1559,6 @@ class TestReasoningEffort: executor.handle_step_observed_low = ( AgentExecutor.handle_step_observed_low.__get__(executor) ) - executor._printer = Mock() todo = TodoItem( step_number=1, diff --git a/lib/crewai/tests/agents/test_lite_agent.py b/lib/crewai/tests/agents/test_lite_agent.py index 5397e6281..b42e2c1ec 100644 --- a/lib/crewai/tests/agents/test_lite_agent.py +++ b/lib/crewai/tests/agents/test_lite_agent.py @@ -1060,27 +1060,13 @@ def test_lite_agent_verbose_false_suppresses_printer_output(): verbose=False, ) - result = agent.kickoff("Say hello") + mock_printer = Mock() + with patch("crewai.lite_agent.PRINTER", mock_printer): + result = agent.kickoff("Say hello") assert result is not None assert isinstance(result, LiteAgentOutput) - # Verify the printer was never called - agent._printer.print = Mock() - # For a clean verification, patch printer before execution - with pytest.warns(DeprecationWarning): - agent2 = LiteAgent( - role="Test Agent", - goal="Test goal", - backstory="Test backstory", - llm=mock_llm, - verbose=False, - ) - - mock_printer = Mock() - agent2._printer = mock_printer - - agent2.kickoff("Say hello") - + # Verify the printer was never called when verbose=False mock_printer.print.assert_not_called() diff --git a/lib/crewai/tests/tools/test_tool_usage.py b/lib/crewai/tests/tools/test_tool_usage.py index b68a41666..ba2e797d9 100644 --- a/lib/crewai/tests/tools/test_tool_usage.py +++ b/lib/crewai/tests/tools/test_tool_usage.py @@ -529,9 +529,6 @@ def test_tool_validate_input_error_event(): mock_task = MagicMock() mock_tools_handler = MagicMock() - # Mock printer - mock_printer = MagicMock() - # Create test tool class TestTool(BaseTool): name: str = "Test Tool" @@ -551,8 +548,6 @@ def test_tool_validate_input_error_event(): agent=mock_agent, action=MagicMock(tool="test_tool"), ) - tool_usage._printer = mock_printer - # Mock all parsing attempts to fail with ( patch("json.loads", side_effect=json.JSONDecodeError("Test Error", "", 0)), diff --git a/lib/crewai/tests/utilities/test_converter.py b/lib/crewai/tests/utilities/test_converter.py index 017f7f8ae..2df350c0d 100644 --- a/lib/crewai/tests/utilities/test_converter.py +++ b/lib/crewai/tests/utilities/test_converter.py @@ -207,10 +207,10 @@ def test_convert_with_instructions_failure( mock_create_converter.return_value = mock_converter result = "Some text to convert" - with patch("crewai.utilities.converter.Printer") as mock_printer: + with patch("crewai.utilities.converter.PRINTER") as mock_printer: output = convert_with_instructions(result, SimpleModel, False, mock_agent) assert output == result - mock_printer.return_value.print.assert_called_once() + mock_printer.print.assert_called_once() # Tests for get_conversion_instructions