From 3546a48922a93cd74d42bfb458ab02b1e315d91d Mon Sep 17 00:00:00 2001 From: Renato Nitta Date: Mon, 20 Apr 2026 18:39:46 -0300 Subject: [PATCH] fix: preserve crew name in events, fallback to class name --- lib/crewai/src/crewai/crew.py | 26 +++++---- lib/crewai/src/crewai/crews/utils.py | 7 ++- lib/crewai/src/crewai/project/annotations.py | 3 + .../evaluators/crew_evaluator_handler.py | 2 +- lib/crewai/tests/test_crew.py | 56 ++++++++++++++++++- lib/crewai/tests/test_project.py | 6 ++ lib/crewai/tests/tracing/test_tracing.py | 2 +- 7 files changed, 87 insertions(+), 15 deletions(-) diff --git a/lib/crewai/src/crewai/crew.py b/lib/crewai/src/crewai/crew.py index 7631a4c2b..1dba43f4b 100644 --- a/lib/crewai/src/crewai/crew.py +++ b/lib/crewai/src/crewai/crew.py @@ -213,7 +213,7 @@ class Crew(FlowTrackable, BaseModel): ) _kickoff_event_id: str | None = PrivateAttr(default=None) - name: str | None = Field(default="crew") + name: str | None = Field(default=None) cache: bool = Field(default=True) tasks: list[Task] = Field(default_factory=list) agents: Annotated[ @@ -853,7 +853,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewTrainStartedEvent( - crew_name=self.name, + crew_name=self.name or self.__class__.__name__, n_iterations=n_iterations, filename=filename, inputs=inputs, @@ -881,7 +881,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewTrainCompletedEvent( - crew_name=self.name, + crew_name=self.name or self.__class__.__name__, n_iterations=n_iterations, filename=filename, ), @@ -889,7 +889,10 @@ class Crew(FlowTrackable, BaseModel): except Exception as e: crewai_event_bus.emit( self, - CrewTrainFailedEvent(error=str(e), crew_name=self.name), + CrewTrainFailedEvent( + error=str(e), + crew_name=self.name or self.__class__.__name__, + ), ) self._logger.log("error", f"Training failed: {e}", color="red") CrewTrainingHandler(TRAINING_DATA_FILE).clear() @@ -974,7 +977,7 @@ class Crew(FlowTrackable, BaseModel): self, CrewKickoffFailedEvent( error=str(e), - crew_name=self.name, + crew_name=self.name or self.__class__.__name__, started_event_id=self._kickoff_event_id, ), ) @@ -1185,7 +1188,7 @@ class Crew(FlowTrackable, BaseModel): self, CrewKickoffFailedEvent( error=str(e), - crew_name=self.name, + crew_name=self.name or self.__class__.__name__, started_event_id=self._kickoff_event_id, ), ) @@ -1791,7 +1794,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewKickoffCompletedEvent( - crew_name=self.name, + crew_name=self.name or self.__class__.__name__, output=final_task_output, total_tokens=self.token_usage.total_tokens, started_event_id=self._kickoff_event_id, @@ -2053,7 +2056,7 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewTestStartedEvent( - crew_name=self.name, + crew_name=self.name or self.__class__.__name__, n_iterations=n_iterations, eval_llm=llm_instance, inputs=inputs, @@ -2072,13 +2075,16 @@ class Crew(FlowTrackable, BaseModel): crewai_event_bus.emit( self, CrewTestCompletedEvent( - crew_name=self.name, + crew_name=self.name or self.__class__.__name__, ), ) except Exception as e: crewai_event_bus.emit( self, - CrewTestFailedEvent(error=str(e), crew_name=self.name), + CrewTestFailedEvent( + error=str(e), + crew_name=self.name or self.__class__.__name__, + ), ) raise diff --git a/lib/crewai/src/crewai/crews/utils.py b/lib/crewai/src/crewai/crews/utils.py index e85a48b05..1df49a278 100644 --- a/lib/crewai/src/crewai/crews/utils.py +++ b/lib/crewai/src/crewai/crews/utils.py @@ -311,7 +311,7 @@ def prepare_kickoff( fmt = ConsoleFormatter(verbose=True) content = fmt.create_status_content( "Resuming from Checkpoint", - crew.name or "Crew", + crew.name or crew.__class__.__name__, "bright_magenta", ID=str(crew.id), ) @@ -319,7 +319,10 @@ def prepare_kickoff( content, "\U0001f504 Resuming from Checkpoint", "bright_magenta" ) else: - started_event = CrewKickoffStartedEvent(crew_name=crew.name, inputs=normalized) + started_event = CrewKickoffStartedEvent( + crew_name=crew.name or crew.__class__.__name__, + inputs=normalized, + ) crew._kickoff_event_id = started_event.event_id future = crewai_event_bus.emit(crew, started_event) if future is not None: diff --git a/lib/crewai/src/crewai/project/annotations.py b/lib/crewai/src/crewai/project/annotations.py index c198c979a..c1ee6cf83 100644 --- a/lib/crewai/src/crewai/project/annotations.py +++ b/lib/crewai/src/crewai/project/annotations.py @@ -238,6 +238,9 @@ 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) + def callback_wrapper( hook: Callable[Concatenate[CrewInstance, P2], R2], instance: CrewInstance ) -> Callable[P2, R2]: 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 9dbfbcb86..669765b6b 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.name, + crew_name=self.crew.name or self.crew.__class__.__name__, crew=self.crew, ), ) diff --git a/lib/crewai/tests/test_crew.py b/lib/crewai/tests/test_crew.py index 9db9ef4e2..795ce9a55 100644 --- a/lib/crewai/tests/test_crew.py +++ b/lib/crewai/tests/test_crew.py @@ -17,6 +17,7 @@ from crewai.crew import Crew from crewai.crews.crew_output import CrewOutput from crewai.events.event_bus import crewai_event_bus from crewai.events.types.crew_events import ( + CrewKickoffStartedEvent, CrewTestCompletedEvent, CrewTestStartedEvent, CrewTrainCompletedEvent, @@ -4738,7 +4739,60 @@ def test_default_crew_name(researcher, writer): Task(description="Task 2", expected_output="output", agent=writer), ], ) - assert crew.name == "crew" + assert crew.name is None + + +def test_crew_kickoff_started_uses_class_name_fallback(researcher, writer): + """Unnamed Crew subclasses should emit their class name in CrewKickoffStartedEvent.""" + from crewai.crews.utils import prepare_kickoff + + class ResearchAutomation(Crew): + pass + + crew = ResearchAutomation( + agents=[researcher, writer], + tasks=[ + Task(description="Task 1", expected_output="output", agent=researcher), + ], + ) + + captured: list[str | None] = [] + with crewai_event_bus.scoped_handlers(): + + @crewai_event_bus.on(CrewKickoffStartedEvent) + def _capture(_source: Any, event: CrewKickoffStartedEvent) -> None: + captured.append(event.crew_name) + + prepare_kickoff(crew, inputs=None) + + assert captured == ["ResearchAutomation"] + + +def test_crew_kickoff_started_respects_explicit_name(researcher, writer): + """Explicitly-named crews should emit the provided name, not the class name.""" + from crewai.crews.utils import prepare_kickoff + + class ResearchAutomation(Crew): + pass + + crew = ResearchAutomation( + name="My Research Automation", + agents=[researcher, writer], + tasks=[ + Task(description="Task 1", expected_output="output", agent=researcher), + ], + ) + + captured: list[str | None] = [] + with crewai_event_bus.scoped_handlers(): + + @crewai_event_bus.on(CrewKickoffStartedEvent) + def _capture(_source: Any, event: CrewKickoffStartedEvent) -> None: + captured.append(event.crew_name) + + prepare_kickoff(crew, inputs=None) + + assert captured == ["My Research Automation"] @pytest.mark.vcr() diff --git a/lib/crewai/tests/test_project.py b/lib/crewai/tests/test_project.py index 9d7f332da..ed2efa08f 100644 --- a/lib/crewai/tests/test_project.py +++ b/lib/crewai/tests/test_project.py @@ -261,6 +261,12 @@ def test_crew_name(): assert crew._crew_name == "InternalCrew" +def test_crew_decorator_propagates_class_name_to_instance(): + """@crew-decorated factory method should set Crew.name to the decorated class name.""" + crew_instance = InternalCrew().crew() + assert crew_instance.name == "InternalCrew" + + @tool def simple_tool(): """Return 'Hi!'""" diff --git a/lib/crewai/tests/tracing/test_tracing.py b/lib/crewai/tests/tracing/test_tracing.py index 640aca832..267b30072 100644 --- a/lib/crewai/tests/tracing/test_tracing.py +++ b/lib/crewai/tests/tracing/test_tracing.py @@ -304,7 +304,7 @@ class TestTraceListenerSetup: # Verify the first completion event has proper structure completion_event = completion_events[0] assert "crew_name" in completion_event.event_data - assert completion_event.event_data["crew_name"] == "crew" + assert completion_event.event_data["crew_name"] == "Crew" # Verify all events have proper structure for call in add_event_mock.call_args_list: