diff --git a/crewai/agent.py b/crewai/agent.py index 31087bdb5..b4c45356c 100644 --- a/crewai/agent.py +++ b/crewai/agent.py @@ -1,3 +1,4 @@ +import uuid from typing import Any, List, Optional from langchain.agents import AgentExecutor @@ -6,14 +7,22 @@ from langchain.chat_models import ChatOpenAI from langchain.memory import ConversationSummaryMemory from langchain.tools.render import render_text_description from langchain_core.runnables.config import RunnableConfig -from pydantic import ConfigDict, Field, InstanceOf, model_validator +from pydantic import ( + UUID4, + BaseModel, + ConfigDict, + Field, + InstanceOf, + field_validator, + model_validator, +) +from pydantic_core import PydanticCustomError from crewai.agents import CacheHandler, CrewAgentOutputParser, ToolsHandler -from crewai.base.model import CrewAIBaseModel from crewai.prompts import Prompts -class Agent(CrewAIBaseModel): +class Agent(BaseModel): """Represents an agent in a system. Each agent has a role, a goal, a backstory, and an optional language model (llm). @@ -30,7 +39,13 @@ class Agent(CrewAIBaseModel): allow_delegation: Whether the agent is allowed to delegate tasks to other agents. """ + __hash__ = object.__hash__ model_config = ConfigDict(arbitrary_types_allowed=True) + id: UUID4 = Field( + default_factory=uuid.uuid4, + frozen=True, + description="Unique identifier for the object, not set by user.", + ) role: str = Field(description="Role of the agent") goal: str = Field(description="Objective of the agent") backstory: str = Field(description="Backstory of the agent") @@ -63,6 +78,14 @@ class Agent(CrewAIBaseModel): default=CacheHandler(), description="An instance of the CacheHandler class." ) + @field_validator("id", mode="before") + @classmethod + def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: + if v: + raise PydanticCustomError( + "may_not_set_field", "This field is not to be set by the user.", {} + ) + @model_validator(mode="after") def check_agent_executor(self) -> "Agent": if not self.agent_executor: diff --git a/crewai/crew.py b/crewai/crew.py index 77902f40c..f26ffad20 100644 --- a/crewai/crew.py +++ b/crewai/crew.py @@ -1,7 +1,10 @@ import json +import uuid from typing import Any, Dict, List, Optional, Union from pydantic import ( + UUID4, + BaseModel, ConfigDict, Field, InstanceOf, @@ -13,15 +16,15 @@ from pydantic_core import PydanticCustomError from crewai.agent import Agent from crewai.agents import CacheHandler -from crewai.base.model import CrewAIBaseModel from crewai.process import Process from crewai.task import Task from crewai.tools.agent_tools import AgentTools -class Crew(CrewAIBaseModel): +class Crew(BaseModel): """Class that represents a group of agents, how they should work together and their tasks.""" + __hash__ = object.__hash__ model_config = ConfigDict(arbitrary_types_allowed=True) tasks: List[Task] = Field(description="List of tasks", default_factory=list) agents: List[Agent] = Field( @@ -39,6 +42,19 @@ class Crew(CrewAIBaseModel): cache_handler: Optional[InstanceOf[CacheHandler]] = Field( default=CacheHandler(), description="An instance of the CacheHandler class." ) + id: UUID4 = Field( + default_factory=uuid.uuid4, + frozen=True, + description="Unique identifier for the object, not set by user.", + ) + + @field_validator("id", mode="before") + @classmethod + def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: + if v: + raise PydanticCustomError( + "may_not_set_field", "This field is not to be set by the user.", {} + ) @classmethod @field_validator("config", mode="before") diff --git a/crewai/task.py b/crewai/task.py index f766f8c0b..7198e99e2 100644 --- a/crewai/task.py +++ b/crewai/task.py @@ -1,14 +1,16 @@ +import uuid from typing import Any, List, Optional -from pydantic import Field, model_validator +from pydantic import UUID4, BaseModel, Field, field_validator, model_validator +from pydantic_core import PydanticCustomError from crewai.agent import Agent -from crewai.base.model import CrewAIBaseModel -class Task(CrewAIBaseModel): +class Task(BaseModel): """Class that represent a task to be executed.""" + __hash__ = object.__hash__ description: str = Field(description="Description of the actual task.") agent: Optional[Agent] = Field( description="Agent responsible for the task.", default=None @@ -17,6 +19,19 @@ class Task(CrewAIBaseModel): default_factory=list, description="Tools the agent are limited to use for this task.", ) + id: UUID4 = Field( + default_factory=uuid.uuid4, + frozen=True, + description="Unique identifier for the object, not set by user.", + ) + + @field_validator("id", mode="before") + @classmethod + def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: + if v: + raise PydanticCustomError( + "may_not_set_field", "This field is not to be set by the user.", {} + ) @model_validator(mode="after") def check_tools(self):