mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-22 06:18:14 +00:00
Compare commits
3 Commits
devin/1768
...
devin/1750
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f03a7481e4 | ||
|
|
189b3bfbf4 | ||
|
|
59f4c71370 |
@@ -1034,7 +1034,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
context = (
|
context = (
|
||||||
aggregate_raw_outputs_from_task_outputs(task_outputs)
|
aggregate_raw_outputs_from_task_outputs(task_outputs)
|
||||||
if task.context is NOT_SPECIFIED
|
if task.context is NOT_SPECIFIED
|
||||||
else aggregate_raw_outputs_from_tasks(task.context)
|
else aggregate_raw_outputs_from_tasks(cast(List["Task"], task.context))
|
||||||
)
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from typing import (
|
|||||||
from pydantic import (
|
from pydantic import (
|
||||||
UUID4,
|
UUID4,
|
||||||
BaseModel,
|
BaseModel,
|
||||||
|
ConfigDict,
|
||||||
Field,
|
Field,
|
||||||
PrivateAttr,
|
PrivateAttr,
|
||||||
field_validator,
|
field_validator,
|
||||||
@@ -39,7 +40,7 @@ from crewai.tasks.output_format import OutputFormat
|
|||||||
from crewai.tasks.task_output import TaskOutput
|
from crewai.tasks.task_output import TaskOutput
|
||||||
from crewai.tools.base_tool import BaseTool
|
from crewai.tools.base_tool import BaseTool
|
||||||
from crewai.utilities.config import process_config
|
from crewai.utilities.config import process_config
|
||||||
from crewai.utilities.constants import NOT_SPECIFIED
|
from crewai.utilities.constants import NOT_SPECIFIED, _NotSpecified
|
||||||
from crewai.utilities.guardrail import process_guardrail, GuardrailResult
|
from crewai.utilities.guardrail import process_guardrail, GuardrailResult
|
||||||
from crewai.utilities.converter import Converter, convert_to_model
|
from crewai.utilities.converter import Converter, convert_to_model
|
||||||
from crewai.utilities.events import (
|
from crewai.utilities.events import (
|
||||||
@@ -78,6 +79,8 @@ class Task(BaseModel):
|
|||||||
used_tools: int = 0
|
used_tools: int = 0
|
||||||
tools_errors: int = 0
|
tools_errors: int = 0
|
||||||
delegations: int = 0
|
delegations: int = 0
|
||||||
|
|
||||||
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||||
i18n: I18N = I18N()
|
i18n: I18N = I18N()
|
||||||
name: Optional[str] = Field(default=None)
|
name: Optional[str] = Field(default=None)
|
||||||
prompt_context: Optional[str] = None
|
prompt_context: Optional[str] = None
|
||||||
@@ -95,7 +98,7 @@ class Task(BaseModel):
|
|||||||
agent: Optional[BaseAgent] = Field(
|
agent: Optional[BaseAgent] = Field(
|
||||||
description="Agent responsible for execution the task.", default=None
|
description="Agent responsible for execution the task.", default=None
|
||||||
)
|
)
|
||||||
context: Optional[List["Task"]] = Field(
|
context: Union[List["Task"], _NotSpecified, None] = Field(
|
||||||
description="Other tasks that will have their output used as context for this task.",
|
description="Other tasks that will have their output used as context for this task.",
|
||||||
default=NOT_SPECIFIED,
|
default=NOT_SPECIFIED,
|
||||||
)
|
)
|
||||||
|
|||||||
103
tests/test_task_context_type_annotation.py
Normal file
103
tests/test_task_context_type_annotation.py
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
"""
|
||||||
|
Test for issue #3019: Type annotation for `context` in `Task` is `Optional[List["Task"]]`, but default is `NOT_SPECIFIED`
|
||||||
|
|
||||||
|
This test reproduces the type annotation issue and verifies the fix.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from typing import get_type_hints, get_origin, get_args
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
|
from crewai.task import Task
|
||||||
|
from crewai.utilities.constants import NOT_SPECIFIED, _NotSpecified
|
||||||
|
|
||||||
|
|
||||||
|
class TestTaskContextTypeAnnotation:
|
||||||
|
"""Test cases for Task context field type annotation issue."""
|
||||||
|
|
||||||
|
def test_task_context_default_value_is_not_specified(self):
|
||||||
|
"""Test that Task.context default value is NOT_SPECIFIED sentinel."""
|
||||||
|
task = Task(description="Test task", expected_output="Test output")
|
||||||
|
assert task.context is NOT_SPECIFIED
|
||||||
|
assert isinstance(task.context, _NotSpecified)
|
||||||
|
|
||||||
|
def test_task_context_can_be_set_to_none(self):
|
||||||
|
"""Test that Task.context can be explicitly set to None."""
|
||||||
|
task = Task(description="Test task", expected_output="Test output", context=None)
|
||||||
|
assert task.context is None
|
||||||
|
|
||||||
|
def test_task_context_can_be_set_to_empty_list(self):
|
||||||
|
"""Test that Task.context can be set to an empty list."""
|
||||||
|
task = Task(description="Test task", expected_output="Test output", context=[])
|
||||||
|
assert task.context == []
|
||||||
|
assert isinstance(task.context, list)
|
||||||
|
|
||||||
|
def test_task_context_can_be_set_to_task_list(self):
|
||||||
|
"""Test that Task.context can be set to a list of tasks."""
|
||||||
|
task1 = Task(description="Task 1", expected_output="Output 1")
|
||||||
|
task2 = Task(description="Task 2", expected_output="Output 2")
|
||||||
|
task3 = Task(description="Task 3", expected_output="Output 3", context=[task1, task2])
|
||||||
|
|
||||||
|
assert task3.context == [task1, task2]
|
||||||
|
assert isinstance(task3.context, list)
|
||||||
|
assert len(task3.context) == 2
|
||||||
|
|
||||||
|
def test_task_context_type_annotation_includes_not_specified(self):
|
||||||
|
"""Test that the type annotation for context includes _NotSpecified type."""
|
||||||
|
type_hints = get_type_hints(Task)
|
||||||
|
context_type = type_hints.get('context')
|
||||||
|
|
||||||
|
assert context_type is not None
|
||||||
|
|
||||||
|
origin = get_origin(context_type)
|
||||||
|
if origin is not None:
|
||||||
|
args = get_args(context_type)
|
||||||
|
|
||||||
|
assert any('_NotSpecified' in str(arg) or arg is _NotSpecified for arg in args), \
|
||||||
|
f"Type annotation should include _NotSpecified, got: {args}"
|
||||||
|
|
||||||
|
def test_task_context_distinguishes_not_passed_from_none(self):
|
||||||
|
"""Test that NOT_SPECIFIED distinguishes between not passed and None."""
|
||||||
|
task_not_passed = Task(description="Test task", expected_output="Test output")
|
||||||
|
|
||||||
|
task_explicit_none = Task(description="Test task", expected_output="Test output", context=None)
|
||||||
|
|
||||||
|
task_empty_list = Task(description="Test task", expected_output="Test output", context=[])
|
||||||
|
|
||||||
|
assert task_not_passed.context is NOT_SPECIFIED
|
||||||
|
assert task_explicit_none.context is None
|
||||||
|
assert task_empty_list.context == []
|
||||||
|
|
||||||
|
assert task_not_passed.context is not task_explicit_none.context
|
||||||
|
assert task_not_passed.context != task_empty_list.context
|
||||||
|
assert task_explicit_none.context != task_empty_list.context
|
||||||
|
|
||||||
|
def test_task_context_usage_in_crew_logic(self):
|
||||||
|
"""Test that the context field works correctly with crew logic."""
|
||||||
|
from crewai.utilities.constants import NOT_SPECIFIED
|
||||||
|
|
||||||
|
task_with_not_specified = Task(description="Task 1", expected_output="Output 1")
|
||||||
|
task_with_none = Task(description="Task 2", expected_output="Output 2", context=None)
|
||||||
|
task_with_empty_list = Task(description="Task 3", expected_output="Output 3", context=[])
|
||||||
|
|
||||||
|
assert task_with_not_specified.context is NOT_SPECIFIED
|
||||||
|
assert task_with_none.context is not NOT_SPECIFIED
|
||||||
|
assert task_with_empty_list.context is not NOT_SPECIFIED
|
||||||
|
|
||||||
|
def test_task_context_repr_shows_not_specified(self):
|
||||||
|
"""Test that NOT_SPECIFIED has a proper string representation."""
|
||||||
|
task = Task(description="Test task", expected_output="Test output")
|
||||||
|
assert str(task.context) == "NOT_SPECIFIED"
|
||||||
|
assert repr(task.context) == "NOT_SPECIFIED"
|
||||||
|
|
||||||
|
def test_task_context_validation_accepts_valid_types(self):
|
||||||
|
"""Test that Task validation accepts all valid context types."""
|
||||||
|
try:
|
||||||
|
Task(description="Test 1", expected_output="Output 1")
|
||||||
|
Task(description="Test 2", expected_output="Output 2", context=None)
|
||||||
|
Task(description="Test 3", expected_output="Output 3", context=[])
|
||||||
|
|
||||||
|
task1 = Task(description="Task 1", expected_output="Output 1")
|
||||||
|
Task(description="Test 4", expected_output="Output 4", context=[task1])
|
||||||
|
except ValidationError as e:
|
||||||
|
pytest.fail(f"Valid context types should not raise ValidationError: {e}")
|
||||||
Reference in New Issue
Block a user