diff --git a/lib/crewai/src/crewai/cli/authentication/utils.py b/lib/crewai/src/crewai/cli/authentication/utils.py index 849dda594..08955092b 100644 --- a/lib/crewai/src/crewai/cli/authentication/utils.py +++ b/lib/crewai/src/crewai/cli/authentication/utils.py @@ -30,6 +30,7 @@ def validate_jwt_token( algorithms=["RS256"], audience=audience, issuer=issuer, + leeway=10.0, options={ "verify_signature": True, "verify_exp": True, diff --git a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml index 45869fab0..f09d72949 100644 --- a/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/crew/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]>=0.203.0,<1.0.0" + "crewai[tools]>=0.203.1,<1.0.0" ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml index dd635656b..7105029c6 100644 --- a/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/flow/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]>=0.203.0,<1.0.0", + "crewai[tools]>=0.203.1,<1.0.0", ] [project.scripts] diff --git a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml index 70d0cf5f9..61d4343b9 100644 --- a/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml +++ b/lib/crewai/src/crewai/cli/templates/tool/pyproject.toml @@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}" readme = "README.md" requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]>=0.203.0" + "crewai[tools]>=0.203.1" ] [tool.crewai] diff --git a/lib/crewai/src/crewai/events/listeners/tracing/utils.py b/lib/crewai/src/crewai/events/listeners/tracing/utils.py index 7b1978912..03089994c 100644 --- a/lib/crewai/src/crewai/events/listeners/tracing/utils.py +++ b/lib/crewai/src/crewai/events/listeners/tracing/utils.py @@ -358,7 +358,8 @@ def prompt_user_for_trace_viewing(timeout_seconds: int = 20) -> bool: try: response = input().strip().lower() result[0] = response in ["y", "yes"] - except (EOFError, KeyboardInterrupt): + except (EOFError, KeyboardInterrupt, OSError, LookupError): + # Handle all input-related errors silently result[0] = False input_thread = threading.Thread(target=get_input, daemon=True) @@ -371,6 +372,7 @@ def prompt_user_for_trace_viewing(timeout_seconds: int = 20) -> bool: return result[0] except Exception: + # Suppress any warnings or errors and assume "no" return False diff --git a/lib/crewai/src/crewai/task.py b/lib/crewai/src/crewai/task.py index 2d2b946ae..1f757fd59 100644 --- a/lib/crewai/src/crewai/task.py +++ b/lib/crewai/src/crewai/task.py @@ -7,7 +7,7 @@ import uuid import warnings from collections.abc import Callable from concurrent.futures import Future -from copy import copy +from copy import copy as shallow_copy from hashlib import md5 from pathlib import Path from typing import ( @@ -674,7 +674,9 @@ Follow these guidelines: copied_data = {k: v for k, v in copied_data.items() if v is not None} cloned_context = ( - [task_mapping[context_task.key] for context_task in self.context] + self.context + if self.context is NOT_SPECIFIED + else [task_mapping[context_task.key] for context_task in self.context] if isinstance(self.context, list) else None ) @@ -683,7 +685,7 @@ Follow these guidelines: return next((agent for agent in agents if agent.role == role), None) cloned_agent = get_agent_by_role(self.agent.role) if self.agent else None - cloned_tools = copy(self.tools) if self.tools else [] + cloned_tools = shallow_copy(self.tools) if self.tools else [] return self.__class__( **copied_data, diff --git a/lib/crewai/tests/cli/authentication/test_utils.py b/lib/crewai/tests/cli/authentication/test_utils.py index 247174c78..5df00db18 100644 --- a/lib/crewai/tests/cli/authentication/test_utils.py +++ b/lib/crewai/tests/cli/authentication/test_utils.py @@ -2,6 +2,7 @@ import unittest from unittest.mock import MagicMock, patch import jwt + from crewai.cli.authentication.utils import validate_jwt_token @@ -16,19 +17,22 @@ class TestUtils(unittest.TestCase): key="mock_signing_key" ) + jwt_token = "aaaaa.bbbbbb.cccccc" # noqa: S105 + decoded_token = validate_jwt_token( - jwt_token="aaaaa.bbbbbb.cccccc", + jwt_token=jwt_token, jwks_url="https://mock_jwks_url", issuer="https://mock_issuer", audience="app_id_xxxx", ) mock_jwt.decode.assert_called_with( - "aaaaa.bbbbbb.cccccc", + jwt_token, "mock_signing_key", algorithms=["RS256"], audience="app_id_xxxx", issuer="https://mock_issuer", + leeway=10.0, options={ "verify_signature": True, "verify_exp": True, @@ -42,9 +46,9 @@ class TestUtils(unittest.TestCase): def test_validate_jwt_token_expired(self, mock_jwt, mock_pyjwkclient): mock_jwt.decode.side_effect = jwt.ExpiredSignatureError - with self.assertRaises(Exception): + with self.assertRaises(Exception): # noqa: B017 validate_jwt_token( - jwt_token="aaaaa.bbbbbb.cccccc", + jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106 jwks_url="https://mock_jwks_url", issuer="https://mock_issuer", audience="app_id_xxxx", @@ -52,9 +56,9 @@ class TestUtils(unittest.TestCase): def test_validate_jwt_token_invalid_audience(self, mock_jwt, mock_pyjwkclient): mock_jwt.decode.side_effect = jwt.InvalidAudienceError - with self.assertRaises(Exception): + with self.assertRaises(Exception): # noqa: B017 validate_jwt_token( - jwt_token="aaaaa.bbbbbb.cccccc", + jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106 jwks_url="https://mock_jwks_url", issuer="https://mock_issuer", audience="app_id_xxxx", @@ -62,9 +66,9 @@ class TestUtils(unittest.TestCase): def test_validate_jwt_token_invalid_issuer(self, mock_jwt, mock_pyjwkclient): mock_jwt.decode.side_effect = jwt.InvalidIssuerError - with self.assertRaises(Exception): + with self.assertRaises(Exception): # noqa: B017 validate_jwt_token( - jwt_token="aaaaa.bbbbbb.cccccc", + jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106 jwks_url="https://mock_jwks_url", issuer="https://mock_issuer", audience="app_id_xxxx", @@ -74,9 +78,9 @@ class TestUtils(unittest.TestCase): self, mock_jwt, mock_pyjwkclient ): mock_jwt.decode.side_effect = jwt.MissingRequiredClaimError - with self.assertRaises(Exception): + with self.assertRaises(Exception): # noqa: B017 validate_jwt_token( - jwt_token="aaaaa.bbbbbb.cccccc", + jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106 jwks_url="https://mock_jwks_url", issuer="https://mock_issuer", audience="app_id_xxxx", @@ -84,9 +88,9 @@ class TestUtils(unittest.TestCase): def test_validate_jwt_token_jwks_error(self, mock_jwt, mock_pyjwkclient): mock_jwt.decode.side_effect = jwt.exceptions.PyJWKClientError - with self.assertRaises(Exception): + with self.assertRaises(Exception): # noqa: B017 validate_jwt_token( - jwt_token="aaaaa.bbbbbb.cccccc", + jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106 jwks_url="https://mock_jwks_url", issuer="https://mock_issuer", audience="app_id_xxxx", @@ -94,9 +98,9 @@ class TestUtils(unittest.TestCase): def test_validate_jwt_token_invalid_token(self, mock_jwt, mock_pyjwkclient): mock_jwt.decode.side_effect = jwt.InvalidTokenError - with self.assertRaises(Exception): + with self.assertRaises(Exception): # noqa: B017 validate_jwt_token( - jwt_token="aaaaa.bbbbbb.cccccc", + jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106 jwks_url="https://mock_jwks_url", issuer="https://mock_issuer", audience="app_id_xxxx", diff --git a/lib/crewai/tests/test_task.py b/lib/crewai/tests/test_task.py index 0e304df54..169088c83 100644 --- a/lib/crewai/tests/test_task.py +++ b/lib/crewai/tests/test_task.py @@ -1218,7 +1218,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") @@ -1635,3 +1635,48 @@ def test_task_interpolation_with_hyphens(): assert "say hello world" in task.prompt() assert result.raw == "Hello, World!" + + +def test_task_copy_with_none_context(): + original_task = Task( + description="Test task", + expected_output="Test output", + context=None + ) + + new_task = original_task.copy(agents=[], task_mapping={}) + assert original_task.context is None + assert new_task.context is None + + +def test_task_copy_with_not_specified_context(): + from crewai.utilities.constants import NOT_SPECIFIED + original_task = Task( + description="Test task", + expected_output="Test output", + ) + + new_task = original_task.copy(agents=[], task_mapping={}) + assert original_task.context is NOT_SPECIFIED + assert new_task.context is NOT_SPECIFIED + + +def test_task_copy_with_list_context(): + """Test that copying a task with list context works correctly.""" + task1 = Task( + description="Task 1", + expected_output="Output 1" + ) + task2 = Task( + description="Task 2", + expected_output="Output 2", + context=[task1] + ) + + task_mapping = {task1.key: task1} + + copied_task2 = task2.copy(agents=[], task_mapping=task_mapping) + + assert isinstance(copied_task2.context, list) + assert len(copied_task2.context) == 1 + assert copied_task2.context[0] is task1