Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/crewai/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,6 @@ def set_attributes_based_on_config(self) -> "Task":
setattr(self, key, value)
return self

@model_validator(mode="after")
def check_tools(self):
"""Check if the tools are set."""
if not self.tools and self.agent and self.agent.tools:
self.tools.extend(self.agent.tools)
return self

@model_validator(mode="after")
def check_output(self):
Expand Down
46 changes: 43 additions & 3 deletions tests/test_crew.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def test_async_task_cannot_include_sequential_async_tasks_in_context(
# This should raise an error because task2 is async and has task1 in its context without a sync task in between
with pytest.raises(
ValueError,
match="Task 'Task 2' is asynchronous and cannot include other sequential asynchronous tasks in its context.",
match=r"Task 'Task 2' is asynchronous and cannot include other sequential asynchronous tasks in its context.",
):
Crew(tasks=[task1, task2, task3, task4, task5], agents=[researcher, writer])

Expand Down Expand Up @@ -238,7 +238,7 @@ def test_context_no_future_tasks(researcher, writer):
# This should raise an error because task1 has a context dependency on a future task (task4)
with pytest.raises(
ValueError,
match="Task 'Task 1' has a context dependency on a future task 'Task 4', which is not allowed.",
match=r"Task 'Task 1' has a context dependency on a future task 'Task 4', which is not allowed.",
):
Crew(tasks=[task1, task2, task3, task4], agents=[researcher, writer])

Expand Down Expand Up @@ -3339,7 +3339,7 @@ def test_replay_with_invalid_task_id():
):
with pytest.raises(
ValueError,
match="Task with id bf5b09c9-69bd-4eb8-be12-f9e5bae31c2d not found in the crew's tasks.",
match=r"Task with id bf5b09c9-69bd-4eb8-be12-f9e5bae31c2d not found in the crew's tasks.",
):
crew.replay("bf5b09c9-69bd-4eb8-be12-f9e5bae31c2d")

Expand Down Expand Up @@ -3814,6 +3814,46 @@ def test_fetch_inputs():
)


def test_hierarchical_crew_does_not_propagate_agent_tools_to_manager():
"""
Test that in hierarchical crews, manager agent doesn't inherit task agents' tools.
This verifies that the check_tools validator doesn't pollute task.tools at creation time.
Fixes issue #3679: https://github.com/crewAIInc/crewAI/issues/3679
"""
from crewai.tools import tool

@tool
def agent_specific_tool() -> str:
"""A tool that should only be available to the specific agent."""
return "agent specific result"

agent_with_tools = Agent(
role="Specialist",
goal="Do specialized work with custom tools",
backstory="You are a specialist with specific tools",
tools=[agent_specific_tool],
allow_delegation=False,
)

# Create a task with an agent that has tools, but don't assign tools to the task
task = Task(
description="Perform a specialized task",
expected_output="Task result",
agent=agent_with_tools,
)

Crew(
agents=[agent_with_tools],
tasks=[task],
process=Process.hierarchical,
manager_llm="gpt-4o",
)

# Verify that task.tools is empty (not populated with agent's tools)
assert task.tools == []
assert len(task.tools) == 0


def test_task_tools_preserve_code_execution_tools():
"""
Test that task tools don't override code execution tools when allow_code_execution=True
Expand Down
10 changes: 7 additions & 3 deletions tests/test_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@


def test_task_tool_reflect_agent_tools():
"""Test that agent tools are available during task execution via crew fallback logic."""
from crewai.tools import tool

@tool
def fake_tool() -> None:
def fake_tool() -> str:
"Fake tool"
return "result"

researcher = Agent(
role="Researcher",
Expand All @@ -40,7 +42,9 @@ def fake_tool() -> None:
agent=researcher,
)

assert task.tools == [fake_tool]
assert task.tools == []

assert researcher.tools == [fake_tool]


def test_task_tool_takes_precedence_over_agent_tools():
Expand Down Expand Up @@ -1218,7 +1222,7 @@ def test_create_directory_false():
assert not resolved_dir.exists()

with pytest.raises(
RuntimeError, match="Directory .* does not exist and create_directory is False"
RuntimeError, match=r"Directory .* does not exist and create_directory is False"
):
task._save_file("test content")

Expand Down