mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-01 23:32:39 +00:00
fixing tests for delegations and coding
This commit is contained in:
@@ -534,9 +534,6 @@ class Crew(BaseModel):
|
|||||||
if not agent.function_calling_llm: # type: ignore # "BaseAgent" has no attribute "function_calling_llm"
|
if not agent.function_calling_llm: # type: ignore # "BaseAgent" has no attribute "function_calling_llm"
|
||||||
agent.function_calling_llm = self.function_calling_llm # type: ignore # "BaseAgent" has no attribute "function_calling_llm"
|
agent.function_calling_llm = self.function_calling_llm # type: ignore # "BaseAgent" has no attribute "function_calling_llm"
|
||||||
|
|
||||||
if agent.allow_code_execution: # type: ignore # BaseAgent" has no attribute "allow_code_execution"
|
|
||||||
agent.tools += agent.get_code_execution_tools() # type: ignore # "BaseAgent" has no attribute "get_code_execution_tools"; maybe "get_delegation_tools"?
|
|
||||||
|
|
||||||
if not agent.step_callback: # type: ignore # "BaseAgent" has no attribute "step_callback"
|
if not agent.step_callback: # type: ignore # "BaseAgent" has no attribute "step_callback"
|
||||||
agent.step_callback = self.step_callback # type: ignore # "BaseAgent" has no attribute "step_callback"
|
agent.step_callback = self.step_callback # type: ignore # "BaseAgent" has no attribute "step_callback"
|
||||||
|
|
||||||
@@ -684,6 +681,7 @@ class Crew(BaseModel):
|
|||||||
goal=i18n.retrieve("hierarchical_manager_agent", "goal"),
|
goal=i18n.retrieve("hierarchical_manager_agent", "goal"),
|
||||||
backstory=i18n.retrieve("hierarchical_manager_agent", "backstory"),
|
backstory=i18n.retrieve("hierarchical_manager_agent", "backstory"),
|
||||||
tools=AgentTools(agents=self.agents).tools(),
|
tools=AgentTools(agents=self.agents).tools(),
|
||||||
|
allow_delegation=True,
|
||||||
llm=self.manager_llm,
|
llm=self.manager_llm,
|
||||||
verbose=self.verbose,
|
verbose=self.verbose,
|
||||||
)
|
)
|
||||||
@@ -726,9 +724,16 @@ class Crew(BaseModel):
|
|||||||
f"No agent available for task: {task.description}. Ensure that either the task has an assigned agent or a manager agent is provided."
|
f"No agent available for task: {task.description}. Ensure that either the task has an assigned agent or a manager agent is provided."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Determine which tools to use - task tools take precedence over agent tools
|
||||||
|
tools_for_task = task.tools if task.tools else agent_to_use.tools or []
|
||||||
|
# Add delegation tools if agent allows delegation
|
||||||
|
if agent_to_use.allow_delegation:
|
||||||
|
tools_for_task = self._prepare_tools(task, tools_for_task)
|
||||||
|
|
||||||
|
# Add code execution tools if agent allows code execution
|
||||||
|
if agent_to_use.allow_code_execution:
|
||||||
|
tools_for_task += agent_to_use.get_code_execution_tools()
|
||||||
|
|
||||||
tools_for_task = agent_to_use.tools or task.tools or []
|
|
||||||
tools_for_task = self._prepare_tools(task, tools_for_task)
|
|
||||||
self._log_task_start(task, agent_to_use.role)
|
self._log_task_start(task, agent_to_use.role)
|
||||||
|
|
||||||
if isinstance(task, ConditionalTask):
|
if isinstance(task, ConditionalTask):
|
||||||
|
|||||||
@@ -579,6 +579,80 @@ def test_task_tools_override_agent_tools():
|
|||||||
assert len(new_researcher.tools) == 1
|
assert len(new_researcher.tools) == 1
|
||||||
assert isinstance(new_researcher.tools[0], TestTool)
|
assert isinstance(new_researcher.tools[0], TestTool)
|
||||||
|
|
||||||
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
|
def test_task_tools_override_agent_tools_with_allow_delegation():
|
||||||
|
"""
|
||||||
|
Test that task tools override agent tools while preserving delegation tools when allow_delegation=True
|
||||||
|
"""
|
||||||
|
from typing import Type
|
||||||
|
from crewai.tools import BaseTool
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
class TestToolInput(BaseModel):
|
||||||
|
query: str = Field(..., description="Query to process")
|
||||||
|
|
||||||
|
class TestTool(BaseTool):
|
||||||
|
name: str = "Test Tool"
|
||||||
|
description: str = "A test tool that just returns the input"
|
||||||
|
args_schema: Type[BaseModel] = TestToolInput
|
||||||
|
|
||||||
|
def _run(self, query: str) -> str:
|
||||||
|
return f"Processed: {query}"
|
||||||
|
|
||||||
|
class AnotherTestTool(BaseTool):
|
||||||
|
name: str = "Another Test Tool"
|
||||||
|
description: str = "Another test tool"
|
||||||
|
args_schema: Type[BaseModel] = TestToolInput
|
||||||
|
|
||||||
|
def _run(self, query: str) -> str:
|
||||||
|
return f"Another processed: {query}"
|
||||||
|
|
||||||
|
# Set up agents with tools and allow_delegation
|
||||||
|
researcher_with_delegation = researcher.model_copy()
|
||||||
|
researcher_with_delegation.allow_delegation = True
|
||||||
|
researcher_with_delegation.tools = [TestTool()]
|
||||||
|
|
||||||
|
writer_for_delegation = writer.model_copy()
|
||||||
|
|
||||||
|
# Create a task with different tools
|
||||||
|
task = Task(
|
||||||
|
description="Write a test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
agent=researcher_with_delegation,
|
||||||
|
tools=[AnotherTestTool()],
|
||||||
|
)
|
||||||
|
|
||||||
|
crew = Crew(
|
||||||
|
agents=[researcher_with_delegation, writer_for_delegation],
|
||||||
|
tasks=[task],
|
||||||
|
process=Process.sequential,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_task_output = TaskOutput(
|
||||||
|
description="Mock description",
|
||||||
|
raw="mocked output",
|
||||||
|
agent="mocked agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
# We mock execute_sync to verify which tools get used at runtime
|
||||||
|
with patch.object(Task, "execute_sync", return_value=mock_task_output) as mock_execute_sync:
|
||||||
|
crew.kickoff()
|
||||||
|
|
||||||
|
# Inspect the call kwargs to verify the actual tools passed to execution
|
||||||
|
_, kwargs = mock_execute_sync.call_args
|
||||||
|
used_tools = kwargs["tools"]
|
||||||
|
|
||||||
|
# Confirm AnotherTestTool is present but TestTool is not
|
||||||
|
assert any(isinstance(tool, AnotherTestTool) for tool in used_tools), "AnotherTestTool should be present"
|
||||||
|
assert not any(isinstance(tool, TestTool) for tool in used_tools), "TestTool should not be present among used tools"
|
||||||
|
|
||||||
|
# Confirm delegation tool(s) are present
|
||||||
|
assert any("delegate" in tool.name.lower() for tool in used_tools), "Delegation tool should be present"
|
||||||
|
|
||||||
|
# Finally, make sure the agent's original tools remain unchanged
|
||||||
|
assert len(researcher_with_delegation.tools) == 1
|
||||||
|
assert isinstance(researcher_with_delegation.tools[0], TestTool)
|
||||||
|
|
||||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
def test_crew_verbose_output(capsys):
|
def test_crew_verbose_output(capsys):
|
||||||
tasks = [
|
tasks = [
|
||||||
@@ -1366,11 +1440,22 @@ def test_code_execution_flag_adds_code_tool_upon_kickoff():
|
|||||||
|
|
||||||
crew = Crew(agents=[programmer], tasks=[task])
|
crew = Crew(agents=[programmer], tasks=[task])
|
||||||
|
|
||||||
with patch.object(Agent, "execute_task") as executor:
|
mock_task_output = TaskOutput(
|
||||||
executor.return_value = "ok"
|
description="Mock description",
|
||||||
|
raw="mocked output",
|
||||||
|
agent="mocked agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(Task, "execute_sync", return_value=mock_task_output) as mock_execute_sync:
|
||||||
crew.kickoff()
|
crew.kickoff()
|
||||||
assert len(programmer.tools) == 1
|
|
||||||
assert programmer.tools[0].__class__ == CodeInterpreterTool
|
# Get the tools that were actually used in execution
|
||||||
|
_, kwargs = mock_execute_sync.call_args
|
||||||
|
used_tools = kwargs["tools"]
|
||||||
|
|
||||||
|
# Verify that exactly one tool was used and it was a CodeInterpreterTool
|
||||||
|
assert len(used_tools) == 1, "Should have exactly one tool"
|
||||||
|
assert isinstance(used_tools[0], CodeInterpreterTool), "Tool should be CodeInterpreterTool"
|
||||||
|
|
||||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
def test_delegation_is_not_enabled_if_there_are_only_one_agent():
|
def test_delegation_is_not_enabled_if_there_are_only_one_agent():
|
||||||
@@ -2792,3 +2877,78 @@ def test_hierarchical_verbose_false_manager_agent():
|
|||||||
|
|
||||||
assert crew.manager_agent is not None
|
assert crew.manager_agent is not None
|
||||||
assert not crew.manager_agent.verbose
|
assert not crew.manager_agent.verbose
|
||||||
|
|
||||||
|
|
||||||
|
def test_task_tools_preserve_code_execution_tools():
|
||||||
|
"""
|
||||||
|
Test that task tools don't override code execution tools when allow_code_execution=True
|
||||||
|
"""
|
||||||
|
from typing import Type
|
||||||
|
from crewai.tools import BaseTool
|
||||||
|
from crewai_tools import CodeInterpreterTool
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
class TestToolInput(BaseModel):
|
||||||
|
"""Input schema for TestTool."""
|
||||||
|
query: str = Field(..., description="Query to process")
|
||||||
|
|
||||||
|
class TestTool(BaseTool):
|
||||||
|
name: str = "Test Tool"
|
||||||
|
description: str = "A test tool that just returns the input"
|
||||||
|
args_schema: Type[BaseModel] = TestToolInput
|
||||||
|
|
||||||
|
def _run(self, query: str) -> str:
|
||||||
|
return f"Processed: {query}"
|
||||||
|
|
||||||
|
# Create a programmer agent with code execution enabled
|
||||||
|
programmer = Agent(
|
||||||
|
role="Programmer",
|
||||||
|
goal="Write code to solve problems.",
|
||||||
|
backstory="You're a programmer who loves to solve problems with code.",
|
||||||
|
allow_delegation=True,
|
||||||
|
allow_code_execution=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a code reviewer agent
|
||||||
|
reviewer = Agent(
|
||||||
|
role="Code Reviewer",
|
||||||
|
goal="Review code for bugs and improvements",
|
||||||
|
backstory="You're an experienced code reviewer who ensures code quality and best practices.",
|
||||||
|
allow_delegation=True,
|
||||||
|
allow_code_execution=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a task with its own tools
|
||||||
|
task = Task(
|
||||||
|
description="Write a program to calculate fibonacci numbers.",
|
||||||
|
expected_output="A working fibonacci calculator.",
|
||||||
|
agent=programmer,
|
||||||
|
tools=[TestTool()]
|
||||||
|
)
|
||||||
|
|
||||||
|
crew = Crew(
|
||||||
|
agents=[programmer, reviewer],
|
||||||
|
tasks=[task],
|
||||||
|
process=Process.sequential,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_task_output = TaskOutput(
|
||||||
|
description="Mock description",
|
||||||
|
raw="mocked output",
|
||||||
|
agent="mocked agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(Task, "execute_sync", return_value=mock_task_output) as mock_execute_sync:
|
||||||
|
crew.kickoff()
|
||||||
|
|
||||||
|
# Get the tools that were actually used in execution
|
||||||
|
_, kwargs = mock_execute_sync.call_args
|
||||||
|
used_tools = kwargs["tools"]
|
||||||
|
|
||||||
|
# Verify all expected tools are present
|
||||||
|
assert any(isinstance(tool, TestTool) for tool in used_tools), "Task's TestTool should be present"
|
||||||
|
assert any(isinstance(tool, CodeInterpreterTool) for tool in used_tools), "CodeInterpreterTool should be present"
|
||||||
|
assert any("delegate" in tool.name.lower() for tool in used_tools), "Delegation tool should be present"
|
||||||
|
|
||||||
|
# Verify the total number of tools (TestTool + CodeInterpreter + 2 delegation tools)
|
||||||
|
assert len(used_tools) == 4, "Should have TestTool, CodeInterpreter, and 2 delegation tools"
|
||||||
|
|||||||
Reference in New Issue
Block a user