mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-10 00:28:31 +00:00
Merge branch 'main' of github.com:crewAIInc/crewAI into release/v1.0.0
This commit is contained in:
@@ -30,6 +30,7 @@ def validate_jwt_token(
|
|||||||
algorithms=["RS256"],
|
algorithms=["RS256"],
|
||||||
audience=audience,
|
audience=audience,
|
||||||
issuer=issuer,
|
issuer=issuer,
|
||||||
|
leeway=10.0,
|
||||||
options={
|
options={
|
||||||
"verify_signature": True,
|
"verify_signature": True,
|
||||||
"verify_exp": True,
|
"verify_exp": True,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
|||||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||||
requires-python = ">=3.10,<3.14"
|
requires-python = ">=3.10,<3.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crewai[tools]>=0.203.0,<1.0.0"
|
"crewai[tools]>=0.203.1,<1.0.0"
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
|||||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||||
requires-python = ">=3.10,<3.14"
|
requires-python = ">=3.10,<3.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crewai[tools]>=0.203.0,<1.0.0",
|
"crewai[tools]>=0.203.1,<1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10,<3.14"
|
requires-python = ">=3.10,<3.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crewai[tools]>=0.203.0"
|
"crewai[tools]>=0.203.1"
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.crewai]
|
[tool.crewai]
|
||||||
|
|||||||
@@ -358,7 +358,8 @@ def prompt_user_for_trace_viewing(timeout_seconds: int = 20) -> bool:
|
|||||||
try:
|
try:
|
||||||
response = input().strip().lower()
|
response = input().strip().lower()
|
||||||
result[0] = response in ["y", "yes"]
|
result[0] = response in ["y", "yes"]
|
||||||
except (EOFError, KeyboardInterrupt):
|
except (EOFError, KeyboardInterrupt, OSError, LookupError):
|
||||||
|
# Handle all input-related errors silently
|
||||||
result[0] = False
|
result[0] = False
|
||||||
|
|
||||||
input_thread = threading.Thread(target=get_input, daemon=True)
|
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]
|
return result[0]
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
|
# Suppress any warnings or errors and assume "no"
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import uuid
|
|||||||
import warnings
|
import warnings
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from concurrent.futures import Future
|
from concurrent.futures import Future
|
||||||
from copy import copy
|
from copy import copy as shallow_copy
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import (
|
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}
|
copied_data = {k: v for k, v in copied_data.items() if v is not None}
|
||||||
|
|
||||||
cloned_context = (
|
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)
|
if isinstance(self.context, list)
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
@@ -683,7 +685,7 @@ Follow these guidelines:
|
|||||||
return next((agent for agent in agents if agent.role == role), None)
|
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_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__(
|
return self.__class__(
|
||||||
**copied_data,
|
**copied_data,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import unittest
|
|||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
|
|
||||||
from crewai.cli.authentication.utils import validate_jwt_token
|
from crewai.cli.authentication.utils import validate_jwt_token
|
||||||
|
|
||||||
|
|
||||||
@@ -16,19 +17,22 @@ class TestUtils(unittest.TestCase):
|
|||||||
key="mock_signing_key"
|
key="mock_signing_key"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
jwt_token = "aaaaa.bbbbbb.cccccc" # noqa: S105
|
||||||
|
|
||||||
decoded_token = validate_jwt_token(
|
decoded_token = validate_jwt_token(
|
||||||
jwt_token="aaaaa.bbbbbb.cccccc",
|
jwt_token=jwt_token,
|
||||||
jwks_url="https://mock_jwks_url",
|
jwks_url="https://mock_jwks_url",
|
||||||
issuer="https://mock_issuer",
|
issuer="https://mock_issuer",
|
||||||
audience="app_id_xxxx",
|
audience="app_id_xxxx",
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_jwt.decode.assert_called_with(
|
mock_jwt.decode.assert_called_with(
|
||||||
"aaaaa.bbbbbb.cccccc",
|
jwt_token,
|
||||||
"mock_signing_key",
|
"mock_signing_key",
|
||||||
algorithms=["RS256"],
|
algorithms=["RS256"],
|
||||||
audience="app_id_xxxx",
|
audience="app_id_xxxx",
|
||||||
issuer="https://mock_issuer",
|
issuer="https://mock_issuer",
|
||||||
|
leeway=10.0,
|
||||||
options={
|
options={
|
||||||
"verify_signature": True,
|
"verify_signature": True,
|
||||||
"verify_exp": True,
|
"verify_exp": True,
|
||||||
@@ -42,9 +46,9 @@ class TestUtils(unittest.TestCase):
|
|||||||
|
|
||||||
def test_validate_jwt_token_expired(self, mock_jwt, mock_pyjwkclient):
|
def test_validate_jwt_token_expired(self, mock_jwt, mock_pyjwkclient):
|
||||||
mock_jwt.decode.side_effect = jwt.ExpiredSignatureError
|
mock_jwt.decode.side_effect = jwt.ExpiredSignatureError
|
||||||
with self.assertRaises(Exception):
|
with self.assertRaises(Exception): # noqa: B017
|
||||||
validate_jwt_token(
|
validate_jwt_token(
|
||||||
jwt_token="aaaaa.bbbbbb.cccccc",
|
jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106
|
||||||
jwks_url="https://mock_jwks_url",
|
jwks_url="https://mock_jwks_url",
|
||||||
issuer="https://mock_issuer",
|
issuer="https://mock_issuer",
|
||||||
audience="app_id_xxxx",
|
audience="app_id_xxxx",
|
||||||
@@ -52,9 +56,9 @@ class TestUtils(unittest.TestCase):
|
|||||||
|
|
||||||
def test_validate_jwt_token_invalid_audience(self, mock_jwt, mock_pyjwkclient):
|
def test_validate_jwt_token_invalid_audience(self, mock_jwt, mock_pyjwkclient):
|
||||||
mock_jwt.decode.side_effect = jwt.InvalidAudienceError
|
mock_jwt.decode.side_effect = jwt.InvalidAudienceError
|
||||||
with self.assertRaises(Exception):
|
with self.assertRaises(Exception): # noqa: B017
|
||||||
validate_jwt_token(
|
validate_jwt_token(
|
||||||
jwt_token="aaaaa.bbbbbb.cccccc",
|
jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106
|
||||||
jwks_url="https://mock_jwks_url",
|
jwks_url="https://mock_jwks_url",
|
||||||
issuer="https://mock_issuer",
|
issuer="https://mock_issuer",
|
||||||
audience="app_id_xxxx",
|
audience="app_id_xxxx",
|
||||||
@@ -62,9 +66,9 @@ class TestUtils(unittest.TestCase):
|
|||||||
|
|
||||||
def test_validate_jwt_token_invalid_issuer(self, mock_jwt, mock_pyjwkclient):
|
def test_validate_jwt_token_invalid_issuer(self, mock_jwt, mock_pyjwkclient):
|
||||||
mock_jwt.decode.side_effect = jwt.InvalidIssuerError
|
mock_jwt.decode.side_effect = jwt.InvalidIssuerError
|
||||||
with self.assertRaises(Exception):
|
with self.assertRaises(Exception): # noqa: B017
|
||||||
validate_jwt_token(
|
validate_jwt_token(
|
||||||
jwt_token="aaaaa.bbbbbb.cccccc",
|
jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106
|
||||||
jwks_url="https://mock_jwks_url",
|
jwks_url="https://mock_jwks_url",
|
||||||
issuer="https://mock_issuer",
|
issuer="https://mock_issuer",
|
||||||
audience="app_id_xxxx",
|
audience="app_id_xxxx",
|
||||||
@@ -74,9 +78,9 @@ class TestUtils(unittest.TestCase):
|
|||||||
self, mock_jwt, mock_pyjwkclient
|
self, mock_jwt, mock_pyjwkclient
|
||||||
):
|
):
|
||||||
mock_jwt.decode.side_effect = jwt.MissingRequiredClaimError
|
mock_jwt.decode.side_effect = jwt.MissingRequiredClaimError
|
||||||
with self.assertRaises(Exception):
|
with self.assertRaises(Exception): # noqa: B017
|
||||||
validate_jwt_token(
|
validate_jwt_token(
|
||||||
jwt_token="aaaaa.bbbbbb.cccccc",
|
jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106
|
||||||
jwks_url="https://mock_jwks_url",
|
jwks_url="https://mock_jwks_url",
|
||||||
issuer="https://mock_issuer",
|
issuer="https://mock_issuer",
|
||||||
audience="app_id_xxxx",
|
audience="app_id_xxxx",
|
||||||
@@ -84,9 +88,9 @@ class TestUtils(unittest.TestCase):
|
|||||||
|
|
||||||
def test_validate_jwt_token_jwks_error(self, mock_jwt, mock_pyjwkclient):
|
def test_validate_jwt_token_jwks_error(self, mock_jwt, mock_pyjwkclient):
|
||||||
mock_jwt.decode.side_effect = jwt.exceptions.PyJWKClientError
|
mock_jwt.decode.side_effect = jwt.exceptions.PyJWKClientError
|
||||||
with self.assertRaises(Exception):
|
with self.assertRaises(Exception): # noqa: B017
|
||||||
validate_jwt_token(
|
validate_jwt_token(
|
||||||
jwt_token="aaaaa.bbbbbb.cccccc",
|
jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106
|
||||||
jwks_url="https://mock_jwks_url",
|
jwks_url="https://mock_jwks_url",
|
||||||
issuer="https://mock_issuer",
|
issuer="https://mock_issuer",
|
||||||
audience="app_id_xxxx",
|
audience="app_id_xxxx",
|
||||||
@@ -94,9 +98,9 @@ class TestUtils(unittest.TestCase):
|
|||||||
|
|
||||||
def test_validate_jwt_token_invalid_token(self, mock_jwt, mock_pyjwkclient):
|
def test_validate_jwt_token_invalid_token(self, mock_jwt, mock_pyjwkclient):
|
||||||
mock_jwt.decode.side_effect = jwt.InvalidTokenError
|
mock_jwt.decode.side_effect = jwt.InvalidTokenError
|
||||||
with self.assertRaises(Exception):
|
with self.assertRaises(Exception): # noqa: B017
|
||||||
validate_jwt_token(
|
validate_jwt_token(
|
||||||
jwt_token="aaaaa.bbbbbb.cccccc",
|
jwt_token="aaaaa.bbbbbb.cccccc", # noqa: S106
|
||||||
jwks_url="https://mock_jwks_url",
|
jwks_url="https://mock_jwks_url",
|
||||||
issuer="https://mock_issuer",
|
issuer="https://mock_issuer",
|
||||||
audience="app_id_xxxx",
|
audience="app_id_xxxx",
|
||||||
|
|||||||
@@ -1218,7 +1218,7 @@ def test_create_directory_false():
|
|||||||
assert not resolved_dir.exists()
|
assert not resolved_dir.exists()
|
||||||
|
|
||||||
with pytest.raises(
|
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")
|
task._save_file("test content")
|
||||||
|
|
||||||
@@ -1635,3 +1635,48 @@ def test_task_interpolation_with_hyphens():
|
|||||||
assert "say hello world" in task.prompt()
|
assert "say hello world" in task.prompt()
|
||||||
|
|
||||||
assert result.raw == "Hello, World!"
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user