From 4b018a2b5bb468d5b33af05f7d0bbcb8acd82c5f Mon Sep 17 00:00:00 2001 From: Renato Nitta Date: Tue, 21 Apr 2026 12:31:06 -0300 Subject: [PATCH] refactor: resolve name via model_validator --- lib/crewai/src/crewai/crew.py | 34 ++++++++++--------- lib/crewai/src/crewai/crews/utils.py | 7 ++-- .../listeners/tracing/trace_listener.py | 6 +--- lib/crewai/src/crewai/project/annotations.py | 3 +- .../evaluators/crew_evaluator_handler.py | 2 +- lib/crewai/tests/test_crew.py | 2 +- 6 files changed, 26 insertions(+), 28 deletions(-) diff --git a/lib/crewai/src/crewai/crew.py b/lib/crewai/src/crewai/crew.py index 7bc7ab5d2..9d16e0480 100644 --- a/lib/crewai/src/crewai/crew.py +++ b/lib/crewai/src/crewai/crew.py @@ -546,6 +546,13 @@ class Crew(FlowTrackable, BaseModel): # TODO: Improve typing return json.loads(v) if isinstance(v, Json) else v # type: ignore + @model_validator(mode="after") + def _resolve_name(self) -> Self: + """Fall back to the class name when no explicit name is provided.""" + if self.name is None: + self.name = type(self).__name__ + return self + @model_validator(mode="after") def set_private_attrs(self) -> Crew: """set private attributes.""" @@ -581,8 +588,8 @@ class Crew(FlowTrackable, BaseModel): """ from crewai.memory.utils import sanitize_scope_name - # Compute sanitized crew name for root_scope - crew_name = sanitize_scope_name(self.name or "crew") + # `name` is guaranteed non-None by the `_resolve_name` validator. + crew_name = sanitize_scope_name(cast(str, self.name)) crew_root_scope = f"/crew/{crew_name}" if self.memory is True: @@ -786,11 +793,6 @@ class Crew(FlowTrackable, BaseModel): ) return self - @property - def display_name(self) -> str: - """Effective crew name for telemetry and UI, falling back to class name.""" - return self.name or type(self).__name__ - @property def key(self) -> str: source: list[str] = [agent.key for agent in self.agents] + [ @@ -858,7 +860,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewTrainStartedEvent( - crew_name=self.display_name, + crew_name=self.name, n_iterations=n_iterations, filename=filename, inputs=inputs, @@ -886,7 +888,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewTrainCompletedEvent( - crew_name=self.display_name, + crew_name=self.name, n_iterations=n_iterations, filename=filename, ), @@ -896,7 +898,7 @@ class Crew(FlowTrackable, BaseModel): self, CrewTrainFailedEvent( error=str(e), - crew_name=self.display_name, + crew_name=self.name, ), ) self._logger.log("error", f"Training failed: {e}", color="red") @@ -982,7 +984,7 @@ class Crew(FlowTrackable, BaseModel): self, CrewKickoffFailedEvent( error=str(e), - crew_name=self.display_name, + crew_name=self.name, started_event_id=self._kickoff_event_id, ), ) @@ -1193,7 +1195,7 @@ class Crew(FlowTrackable, BaseModel): self, CrewKickoffFailedEvent( error=str(e), - crew_name=self.display_name, + crew_name=self.name, started_event_id=self._kickoff_event_id, ), ) @@ -1799,7 +1801,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewKickoffCompletedEvent( - crew_name=self.display_name, + crew_name=self.name, output=final_task_output, total_tokens=self.token_usage.total_tokens, started_event_id=self._kickoff_event_id, @@ -2061,7 +2063,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewTestStartedEvent( - crew_name=self.display_name, + crew_name=self.name, n_iterations=n_iterations, eval_llm=llm_instance, inputs=inputs, @@ -2080,7 +2082,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewTestCompletedEvent( - crew_name=self.display_name, + crew_name=self.name, ), ) except Exception as e: @@ -2088,7 +2090,7 @@ class Crew(FlowTrackable, BaseModel): self, CrewTestFailedEvent( error=str(e), - crew_name=self.display_name, + crew_name=self.name, ), ) raise diff --git a/lib/crewai/src/crewai/crews/utils.py b/lib/crewai/src/crewai/crews/utils.py index 70b6874c7..1ecf586a8 100644 --- a/lib/crewai/src/crewai/crews/utils.py +++ b/lib/crewai/src/crewai/crews/utils.py @@ -5,7 +5,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Coroutine, Iterable, Mapping from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, cast from opentelemetry import baggage @@ -309,9 +309,10 @@ def prepare_kickoff( from crewai.events.utils.console_formatter import ConsoleFormatter fmt = ConsoleFormatter(verbose=True) + # `name` is guaranteed non-None by the `_resolve_name` validator. content = fmt.create_status_content( "Resuming from Checkpoint", - crew.display_name, + cast(str, crew.name), "bright_magenta", ID=str(crew.id), ) @@ -320,7 +321,7 @@ def prepare_kickoff( ) else: started_event = CrewKickoffStartedEvent( - crew_name=crew.display_name, + crew_name=crew.name, inputs=normalized, ) crew._kickoff_event_id = started_event.event_id 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 8c20eba23..d45f705a1 100644 --- a/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py +++ b/lib/crewai/src/crewai/events/listeners/tracing/trace_listener.py @@ -791,11 +791,7 @@ class TraceCollectionListener(BaseEventListener): if not self.batch_manager.is_batch_initialized(): user_context = self._get_user_context() execution_metadata = { - "crew_name": ( - getattr(source, "display_name", None) - or getattr(source, "name", None) - or "Unknown Crew" - ), + "crew_name": getattr(source, "name", None) or "Unknown Crew", "crewai_version": get_crewai_version(), } self._initialize_batch(user_context, execution_metadata) diff --git a/lib/crewai/src/crewai/project/annotations.py b/lib/crewai/src/crewai/project/annotations.py index c1ee6cf83..77e57f912 100644 --- a/lib/crewai/src/crewai/project/annotations.py +++ b/lib/crewai/src/crewai/project/annotations.py @@ -238,8 +238,7 @@ def crew( crew_instance: Crew = _call_method(meth, self, *args, **kwargs) - if crew_instance.name is None: - crew_instance.name = getattr(self, "_crew_name", None) + crew_instance.name = getattr(self, "_crew_name", None) or crew_instance.name def callback_wrapper( hook: Callable[Concatenate[CrewInstance, P2], R2], instance: CrewInstance diff --git a/lib/crewai/src/crewai/utilities/evaluators/crew_evaluator_handler.py b/lib/crewai/src/crewai/utilities/evaluators/crew_evaluator_handler.py index 5f469c313..9dbfbcb86 100644 --- a/lib/crewai/src/crewai/utilities/evaluators/crew_evaluator_handler.py +++ b/lib/crewai/src/crewai/utilities/evaluators/crew_evaluator_handler.py @@ -213,7 +213,7 @@ class CrewEvaluator: quality=quality_score, execution_duration=current_task.execution_duration, model=getattr(self.llm, "model", str(self.llm)), - crew_name=self.crew.display_name, + crew_name=self.crew.name, crew=self.crew, ), ) diff --git a/lib/crewai/tests/test_crew.py b/lib/crewai/tests/test_crew.py index 5ca716765..96f8781e3 100644 --- a/lib/crewai/tests/test_crew.py +++ b/lib/crewai/tests/test_crew.py @@ -4740,7 +4740,7 @@ def test_default_crew_name(researcher, writer): Task(description="Task 2", expected_output="output", agent=writer), ], ) - assert crew.name is None + assert crew.name == "Crew" @pytest.mark.parametrize(