Compare commits

...

3 Commits

Author SHA1 Message Date
Tony Kipkemboi
90359dbbfb Merge branch 'main' into devin/1745784489-fix-crewstructuredtool-task-compatibility 2025-04-28 07:26:33 -07:00
Devin AI
1133994ec7 fix: update type annotations in agent.py for CrewStructuredTool compatibility
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-04-27 20:21:40 +00:00
Devin AI
aa1d04af41 fix: allow CrewStructuredTool to be used with Task's tools parameter
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-04-27 20:15:40 +00:00
4 changed files with 77 additions and 7 deletions

View File

@@ -17,6 +17,7 @@ from crewai.security import Fingerprint
from crewai.task import Task
from crewai.tools import BaseTool
from crewai.tools.agent_tools.agent_tools import AgentTools
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.utilities import Converter, Prompts
from crewai.utilities.agent_utils import (
get_tool_names,
@@ -185,7 +186,7 @@ class Agent(BaseAgent):
self,
task: Task,
context: Optional[str] = None,
tools: Optional[List[BaseTool]] = None
tools: Optional[List[Union[BaseTool, CrewStructuredTool]]] = None
) -> str:
"""Execute a task with the agent.
@@ -406,14 +407,14 @@ class Agent(BaseAgent):
)["output"]
def create_agent_executor(
self, tools: Optional[List[BaseTool]] = None, task=None
self, tools: Optional[List[Union[BaseTool, CrewStructuredTool]]] = None, task=None
) -> None:
"""Create an agent executor for the agent.
Returns:
An instance of the CrewAgentExecutor class.
"""
raw_tools: List[BaseTool] = tools or self.tools or []
raw_tools: List[Union[BaseTool, CrewStructuredTool]] = tools or self.tools or []
parsed_tools = parse_tools(raw_tools)
prompt = Prompts(

View File

@@ -40,6 +40,7 @@ from crewai.tasks.guardrail_result import GuardrailResult
from crewai.tasks.output_format import OutputFormat
from crewai.tasks.task_output import TaskOutput
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.utilities.config import process_config
from crewai.utilities.converter import Converter, convert_to_model
from crewai.utilities.events import (
@@ -72,7 +73,11 @@ class Task(BaseModel):
security_config: Security configuration including fingerprinting.
tools: List of tools/resources limited for task execution.
"""
model_config = {
"arbitrary_types_allowed": True,
}
__hash__ = object.__hash__ # type: ignore
logger: ClassVar[logging.Logger] = logging.getLogger(__name__)
used_tools: int = 0
@@ -118,7 +123,7 @@ class Task(BaseModel):
output: Optional[TaskOutput] = Field(
description="Task output, it's final result after being executed", default=None
)
tools: Optional[List[BaseTool]] = Field(
tools: Optional[List[Union[BaseTool, CrewStructuredTool]]] = Field(
default_factory=list,
description="Tools the agent is limited to use for this task.",
)

View File

@@ -21,15 +21,17 @@ from crewai.utilities.exceptions.context_window_exceeding_exception import (
)
def parse_tools(tools: List[BaseTool]) -> List[CrewStructuredTool]:
def parse_tools(tools: List[Union[BaseTool, CrewStructuredTool]]) -> List[CrewStructuredTool]:
"""Parse tools to be used for the task."""
tools_list = []
for tool in tools:
if isinstance(tool, CrewAITool):
tools_list.append(tool.to_structured_tool())
elif isinstance(tool, CrewStructuredTool):
tools_list.append(tool)
else:
raise ValueError("Tool is not a CrewStructuredTool or BaseTool")
raise ValueError("Tool must be an instance of BaseTool or CrewStructuredTool")
return tools_list

View File

@@ -0,0 +1,62 @@
import pytest
from pydantic import BaseModel, Field
from crewai.task import Task
from crewai.tools.structured_tool import CrewStructuredTool
@pytest.fixture
def simple_tool_function():
def test_func(param1: str, param2: int = 0) -> str:
"""Test function with basic params."""
return f"{param1} {param2}"
return test_func
def test_task_with_structured_tool(simple_tool_function):
"""Test that CrewStructuredTool can be used directly with Task."""
tool = CrewStructuredTool.from_function(
func=simple_tool_function,
name="test_tool",
description="Test tool description"
)
task = Task(
description="Test task description",
expected_output="Expected output",
tools=[tool]
)
assert len(task.tools) == 1
assert task.tools[0] == tool
def test_mixed_tool_types(simple_tool_function):
"""Test that both BaseTool and CrewStructuredTool can be used together with Task."""
from crewai.tools import BaseTool
structured_tool = CrewStructuredTool.from_function(
func=simple_tool_function,
name="structured_tool",
description="Structured tool description"
)
class TestBaseTool(BaseTool):
name: str = "base_tool"
description: str = "Base tool description"
def _run(self, query: str) -> str:
return f"Result for {query}"
base_tool = TestBaseTool()
task = Task(
description="Test task description",
expected_output="Expected output",
tools=[structured_tool, base_tool]
)
assert len(task.tools) == 2
assert task.tools[0] == structured_tool
assert isinstance(task.tools[1], BaseTool)