Skip to content

Commit 80f0011

Browse files
devin-ai-integration[bot]Joe Moura
and
Joe Moura
committed
Fix issue #2392: Preserve ConditionalTask type in Crew.copy() and kickoff_for_each()
Co-Authored-By: Joe Moura <joao@crewai.com>
1 parent e723e5c commit 80f0011

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

src/crewai/task.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,9 @@ def get_agent_by_role(role: str) -> Union["BaseAgent", None]:
641641
cloned_agent = get_agent_by_role(self.agent.role) if self.agent else None
642642
cloned_tools = copy(self.tools) if self.tools else []
643643

644-
copied_task = Task(
644+
# Use the actual class of the instance being copied, not just Task
645+
task_class = self.__class__
646+
copied_task = task_class(
645647
**copied_data,
646648
context=cloned_context,
647649
agent=cloned_agent,

tests/test_conditional_task_copy.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import pytest
2+
from crewai import Agent, Crew, Task
3+
from crewai.tasks.conditional_task import ConditionalTask
4+
from crewai.tasks.task_output import TaskOutput
5+
6+
def test_conditional_task_preserved_in_copy():
7+
"""Test that ConditionalTask objects are preserved when copying a Crew."""
8+
agent = Agent(
9+
role="Researcher",
10+
goal="Research topics",
11+
backstory="You are a researcher."
12+
)
13+
14+
# Create a regular task
15+
task1 = Task(
16+
description="Research topic A",
17+
expected_output="Research results for topic A",
18+
agent=agent
19+
)
20+
21+
# Create a conditional task
22+
conditional_task = ConditionalTask(
23+
description="Research topic B if topic A was successful",
24+
expected_output="Research results for topic B",
25+
agent=agent,
26+
condition=lambda output: "success" in output.raw.lower()
27+
)
28+
29+
# Create a crew with both tasks
30+
crew = Crew(
31+
agents=[agent],
32+
tasks=[task1, conditional_task]
33+
)
34+
35+
# Create a copy of the crew
36+
crew_copy = crew.copy()
37+
38+
# Check that the conditional task is still a ConditionalTask in the copied crew
39+
assert isinstance(crew_copy.tasks[1], ConditionalTask)
40+
assert hasattr(crew_copy.tasks[1], "should_execute")
41+
42+
def test_conditional_task_preserved_in_kickoff_for_each():
43+
"""Test that ConditionalTask objects are preserved when using kickoff_for_each."""
44+
from unittest.mock import patch
45+
46+
agent = Agent(
47+
role="Researcher",
48+
goal="Research topics",
49+
backstory="You are a researcher."
50+
)
51+
52+
# Create a regular task
53+
task1 = Task(
54+
description="Research topic A",
55+
expected_output="Research results for topic A",
56+
agent=agent
57+
)
58+
59+
# Create a conditional task
60+
conditional_task = ConditionalTask(
61+
description="Research topic B if topic A was successful",
62+
expected_output="Research results for topic B",
63+
agent=agent,
64+
condition=lambda output: "success" in output.raw.lower()
65+
)
66+
67+
# Create a crew with both tasks
68+
crew = Crew(
69+
agents=[agent],
70+
tasks=[task1, conditional_task]
71+
)
72+
73+
# Mock the kickoff method to avoid actual execution
74+
with patch.object(Crew, "kickoff") as mock_kickoff:
75+
# Set up the mock to return a TaskOutput
76+
mock_output = TaskOutput(
77+
description="Mock task output",
78+
raw="Success with topic",
79+
agent=agent.role
80+
)
81+
mock_kickoff.return_value = mock_output
82+
83+
# Call kickoff_for_each with test inputs
84+
inputs = [{"topic": "test1"}, {"topic": "test2"}]
85+
crew.kickoff_for_each(inputs=inputs)
86+
87+
# Verify the mock was called with the expected inputs
88+
assert mock_kickoff.call_count == len(inputs)
89+
90+
# Create a copy of the crew to verify the type preservation
91+
# (since we can't directly access the crews created inside kickoff_for_each)
92+
crew_copy = crew.copy()
93+
assert isinstance(crew_copy.tasks[1], ConditionalTask)

0 commit comments

Comments
 (0)