Compare commits

...

2 Commits

Author SHA1 Message Date
Greyson LaLonde
1dd059a395 fix: resolve forward refs for CrewAgentExecutor BaseModel 2026-03-31 08:29:19 +08:00
Greyson LaLonde
297f7a0426 refactor: convert CrewAgentExecutor to BaseModel 2026-03-31 08:22:53 +08:00
2 changed files with 53 additions and 89 deletions

View File

@@ -6,6 +6,7 @@ import warnings
from crewai.agent.core import Agent
from crewai.agent.planning_config import PlanningConfig
from crewai.agents.crew_agent_executor import CrewAgentExecutor
from crewai.crew import Crew
from crewai.crews.crew_output import CrewOutput
from crewai.flow.flow import Flow
@@ -19,6 +20,9 @@ from crewai.tasks.task_output import TaskOutput
from crewai.telemetry.telemetry import Telemetry
CrewAgentExecutor.model_rebuild()
def _suppress_pydantic_deprecation_warnings() -> None:
"""Suppress Pydantic deprecation warnings using targeted monkey patch."""
original_warn = warnings.warn

View File

@@ -14,8 +14,15 @@ import inspect
import logging
from typing import TYPE_CHECKING, Any, Literal, cast
from pydantic import BaseModel, GetCoreSchemaHandler, ValidationError
from pydantic_core import CoreSchema, core_schema
from pydantic import (
BaseModel,
ConfigDict,
Field,
PrivateAttr,
ValidationError,
model_validator,
)
from typing_extensions import Self
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
from crewai.agents.parser import (
@@ -23,6 +30,7 @@ from crewai.agents.parser import (
AgentFinish,
OutputParserError,
)
from crewai.agents.tools_handler import ToolsHandler
from crewai.core.providers.human_input import ExecutorContext, get_provider
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.logging_events import (
@@ -38,6 +46,9 @@ from crewai.hooks.tool_hooks import (
get_after_tool_call_hooks,
get_before_tool_call_hooks,
)
from crewai.llms.base_llm import BaseLLM
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.utilities.agent_utils import (
aget_llm_response,
convert_tools_to_openai_schema,
@@ -59,106 +70,65 @@ 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.prompts import StandardPromptResult, SystemPromptResult
from crewai.utilities.string_utils import sanitize_tool_name
from crewai.utilities.tool_utils import (
aexecute_tool_and_check_finality,
execute_tool_and_check_finality,
)
from crewai.utilities.training_handler import CrewTrainingHandler
from crewai.utilities.types import LLMMessage
logger = logging.getLogger(__name__)
if TYPE_CHECKING:
from crewai.agent import Agent
from crewai.agents.tools_handler import ToolsHandler
from crewai.crew import Crew
from crewai.llms.base_llm import BaseLLM
from crewai.task import Task
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.tools.tool_types import ToolResult
from crewai.utilities.prompts import StandardPromptResult, SystemPromptResult
from crewai.utilities.types import LLMMessage
class CrewAgentExecutor(CrewAgentExecutorMixin):
class CrewAgentExecutor(BaseModel, CrewAgentExecutorMixin):
"""Executor for crew agents.
Manages the execution lifecycle of an agent including prompt formatting,
LLM interactions, tool execution, and feedback handling.
"""
def __init__(
self,
llm: BaseLLM,
task: Task,
crew: Crew,
agent: Agent,
prompt: SystemPromptResult | StandardPromptResult,
max_iter: int,
tools: list[CrewStructuredTool],
tools_names: str,
stop_words: list[str],
tools_description: str,
tools_handler: ToolsHandler,
step_callback: Any = None,
original_tools: list[BaseTool] | None = None,
function_calling_llm: BaseLLM | Any | None = None,
respect_context_window: bool = False,
request_within_rpm_limit: Callable[[], bool] | None = None,
callbacks: list[Any] | None = None,
response_model: type[BaseModel] | None = None,
i18n: I18N | None = None,
) -> None:
"""Initialize executor.
model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True)
Args:
llm: Language model instance.
task: Task to execute.
crew: Crew instance.
agent: Agent to execute.
prompt: Prompt templates.
max_iter: Maximum iterations.
tools: Available tools.
tools_names: Tool names string.
stop_words: Stop word list.
tools_description: Tool descriptions.
tools_handler: Tool handler instance.
step_callback: Optional step callback.
original_tools: Original tool list.
function_calling_llm: Optional function calling LLM.
respect_context_window: Respect context limits.
request_within_rpm_limit: RPM limit check function.
callbacks: Optional callbacks list.
response_model: Optional Pydantic model for structured outputs.
"""
self._i18n: I18N = i18n or get_i18n()
self.llm = llm
self.task = task
self.agent = agent
self.crew = crew
self.prompt = prompt
self.tools = tools
self.tools_names = tools_names
self.stop = stop_words
self.max_iter = max_iter
self.callbacks = callbacks or []
self._printer: Printer = Printer()
self.tools_handler = tools_handler
self.original_tools = original_tools or []
self.step_callback = step_callback
self.tools_description = tools_description
self.function_calling_llm = function_calling_llm
self.respect_context_window = respect_context_window
self.request_within_rpm_limit = request_within_rpm_limit
self.response_model = response_model
self.ask_for_human_input = False
self.messages: list[LLMMessage] = []
self.iterations = 0
self.log_error_after = 3
self.before_llm_call_hooks: list[Callable[..., Any]] = []
self.after_llm_call_hooks: list[Callable[..., Any]] = []
llm: BaseLLM
task: Task | None = None
crew: Crew | None = None
agent: Agent
prompt: SystemPromptResult | StandardPromptResult
max_iter: int
tools: list[CrewStructuredTool]
tools_names: str
stop: list[str] = Field(alias="stop_words")
tools_description: str
tools_handler: ToolsHandler
step_callback: Any = None
original_tools: list[BaseTool] = Field(default_factory=list)
function_calling_llm: BaseLLM | Any | None = None
respect_context_window: bool = False
request_within_rpm_limit: Callable[[], bool] | None = None
callbacks: list[Any] = Field(default_factory=list)
response_model: type[BaseModel] | None = None
i18n: I18N | None = Field(default=None, exclude=True)
ask_for_human_input: bool = False
messages: list[LLMMessage] = Field(default_factory=list)
iterations: int = 0
log_error_after: int = 3
before_llm_call_hooks: list[Callable[..., Any]] = Field(default_factory=list)
after_llm_call_hooks: list[Callable[..., Any]] = Field(default_factory=list)
_i18n: I18N = PrivateAttr()
_printer: Printer = PrivateAttr(default_factory=Printer)
@model_validator(mode="after")
def _init_executor(self) -> Self:
self._i18n = self.i18n or get_i18n()
self.before_llm_call_hooks.extend(get_before_llm_call_hooks())
self.after_llm_call_hooks.extend(get_after_llm_call_hooks())
if self.llm:
@@ -171,6 +141,7 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
else self.stop
)
)
return self
@property
def use_stop_words(self) -> bool:
@@ -1687,14 +1658,3 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
return format_message_for_llm(
self._i18n.slice("feedback_instructions").format(feedback=feedback)
)
@classmethod
def __get_pydantic_core_schema__(
cls, _source_type: Any, _handler: GetCoreSchemaHandler
) -> CoreSchema:
"""Generate Pydantic core schema for BaseClient Protocol.
This allows the Protocol to be used in Pydantic models without
requiring arbitrary_types_allowed=True.
"""
return core_schema.any_schema()