refactor: resolve name via model_validator

This commit is contained in:
Renato Nitta
2026-04-21 12:31:06 -03:00
parent 402f45078a
commit 4b018a2b5b
6 changed files with 26 additions and 28 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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,
),
)

View File

@@ -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(