-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: handle multiple task outputs correctly in conditional tasks #1937
base: main
Are you sure you want to change the base?
Conversation
- Fix IndexError in _handle_conditional_task by using first output - Modify _execute_tasks to accumulate task outputs instead of resetting - Update _create_crew_output to handle multiple outputs correctly - Add tests for multiple tasks with conditional and multiple conditional tasks Co-Authored-By: [email protected] <[email protected]>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
⚙️ Control Options:
|
Disclaimer: This review was made by a crew of AI Agents. Code Review Comment for PR #1937OverviewThis pull request addresses key issues related to handling multiple task outputs in conditional tasks. The modifications improve both the logic in the source code and the coverage of test cases. Below are detailed insights regarding the code changes, historical context from related PRs, implications for related files, and suggestions for improvement. 1. src/crewai/crew.py Changesa) Task Output AccumulationPrevious Code: task_outputs = [task_output] This implementation overwrote previous task outputs, leading to potential data loss. Improved Code: task_outputs.append(task_output) ✅ This is a positive change that successfully accumulates outputs from multiple tasks, preserving critical data. b) Conditional Task HandlingPrevious Code: previous_output = task_outputs[task_index - 1] if task_outputs else None This could lead to an IndexError with insufficient outputs available. Improved Code: previous_output = task_outputs[0] if task_outputs else None Recommendation: relevant_output = task_outputs[-1] if task_outputs else None This ensures we assess the most relevant task output and promote code readability. c) Crew Output CreationPrevious Code: if len(task_outputs) != 1:
raise ValueError("Something went wrong. Kickoff should return only one task output.") This assumes a singular output exists, which conflicts with the new logic accommodating multiple outputs. Improved Code: final_task_output = task_outputs[-1] if task_outputs else None
if not final_task_output:
raise ValueError("No task outputs available to create crew output.") This change efficiently addresses multiple outputs but could benefit from clearer error messaging to improve user experience. 2. tests/crew_test.py AdditionsPositive Aspects
Suggested ImprovementsConsider implementing edge cases such as: @pytest.mark.vcr(filter_headers=["authorization"])
def test_conditional_task_with_empty_outputs():
"""Test handling of conditional tasks with empty previous outputs"""
...
with pytest.raises(ValueError, match="No previous output available"):
crew.kickoff() This would further test the robustness of the new logic under varying conditions. 3. General Recommendations
4. Implementation Concerns
ConclusionThe proposed changes effectively rectify the handling of multiple task outputs, enhancing the overall functionality of the crew module. While the code structure is sound and aligns well with the intended functionality, further refinements in error handling, logging, documentation, and edge case scenarios remain recommended. These modifications also align with insights from related pull requests where similar challenges have been addressed, fostering a more resilient and maintainable codebase. |
…y task check Co-Authored-By: [email protected] <[email protected]>
src/crewai/crew.py
Outdated
@@ -783,7 +797,7 @@ def _handle_conditional_task( | |||
task_outputs = self._process_async_tasks(futures, was_replayed) | |||
futures.clear() | |||
|
|||
previous_output = task_outputs[task_index - 1] if task_outputs else None | |||
previous_output = task_outputs[0] if task_outputs else None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Devin this should be previous_output = task_outputs[-1] if task_outputs else None
instead.
Grab the last element in the list. not the first.
src/crewai/crew.py
Outdated
@@ -918,7 +936,7 @@ def _create_crew_output(self, task_outputs: List[TaskOutput]) -> CrewOutput: | |||
raw=final_task_output.raw, | |||
pydantic=final_task_output.pydantic, | |||
json_dict=final_task_output.json_dict, | |||
tasks_output=[task.output for task in self.tasks if task.output], | |||
tasks_output=task_outputs, # Keep all task outputs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drop comment.
Devin, we need to add additional tests:
|
…tion test Co-Authored-By: [email protected] <[email protected]>
…ub.com/crewAIInc/crewAI into devin/1737479945-fix-conditional-index
tests/crew_test.py
Outdated
|
||
# Set up mocks to return different outputs for each task | ||
with patch.object(Task, "execute_sync") as mock_execute: | ||
mock_execute.side_effect = [mock_first, mock_second, mock_first] # Third value is for conditional task if it runs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you doing [mock_first, mock_second, mock_first]
instead of `[mock_first, mock_second, mock_conditional_task]
You aren't even really testing the conditional task at all. You also aren't checking that the conditional task is properly using the previous output.
The way you could update this task is to create 3 task:
- Task 1: regular task
- Task 2: Conditional Task where the condition is never met
- Task 3: Conditional task where the condition is met.
In this test, you want to make sure that task 3 is getting the previous output from task 1 because that was the last successfully executed task.
assert len(result.tasks_output) == 3 | ||
|
||
@pytest.mark.vcr(filter_headers=["authorization"]) | ||
def test_multiple_conditional_tasks(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is basically the exact same as the one above it so we don't really need it.
Instead, let's change this test to verify that the result we get from crew.kickoff() properly contains all the task outputs. For example, if we have 3 tasks where 1 is normal and 2 are conditional tasks and 1 of those conditioanl tasks is never triggered, the task_outputs from crew.kickoff should only have 2 task outputs.
Fix Multiple Task Outputs in Conditional Tasks
Description
This PR fixes an issue where using multiple conditional tasks, or having two tasks before a conditional task, causes an IndexError in
_handle_conditional_task
. The root cause was that task outputs were being reset instead of accumulated, and the task output indexing was incorrect.Changes
_handle_conditional_task
by using first output instead of task_index - 1_execute_tasks
to accumulate task outputs instead of resetting them_create_crew_output
to handle multiple outputs correctlyTests Added
test_multiple_tasks_with_conditional
: Verifies that having multiple tasks before a conditional task works correctlytest_multiple_conditional_tasks
: Verifies that having multiple conditional tasks in sequence works correctlyLink to Devin run: https://app.devin.ai/sessions/551953893ee74e29ad18839ccc39bb87
Closes #1928