mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-02 07:42:40 +00:00
Add CrewAIPlatformTools (#449)
* chore: add deprecation warning in CrewaiEnterpriseTools * feat: add CrewAI Platform Tool * feat: drop support to oldest env-var token
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch, Mock
|
||||
import pytest
|
||||
from crewai_tools.tools.crewai_platform_tools import CrewAIPlatformActionTool
|
||||
|
||||
|
||||
class TestCrewAIPlatformActionTool(unittest.TestCase):
|
||||
@pytest.fixture
|
||||
def sample_action_schema(self):
|
||||
return {
|
||||
"function": {
|
||||
"name": "test_action",
|
||||
"description": "Test action for unit testing",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Message to send"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"description": "Priority level"
|
||||
}
|
||||
},
|
||||
"required": ["message"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def platform_action_tool(self, sample_action_schema):
|
||||
return CrewAIPlatformActionTool(
|
||||
description="Test Action Tool\nTest description",
|
||||
action_name="test_action",
|
||||
action_schema=sample_action_schema
|
||||
)
|
||||
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool.requests.post")
|
||||
def test_run_success(self, mock_post):
|
||||
schema = {
|
||||
"function": {
|
||||
"name": "test_action",
|
||||
"description": "Test action",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {"type": "string", "description": "Message"}
|
||||
},
|
||||
"required": ["message"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tool = CrewAIPlatformActionTool(
|
||||
description="Test tool",
|
||||
action_name="test_action",
|
||||
action_schema=schema
|
||||
)
|
||||
|
||||
mock_response = Mock()
|
||||
mock_response.ok = True
|
||||
mock_response.json.return_value = {"result": "success", "data": "test_data"}
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
result = tool._run(message="test message")
|
||||
|
||||
mock_post.assert_called_once()
|
||||
_, kwargs = mock_post.call_args
|
||||
|
||||
assert "test_action/execute" in kwargs["url"]
|
||||
assert kwargs["headers"]["Authorization"] == "Bearer test_token"
|
||||
assert kwargs["json"]["message"] == "test message"
|
||||
assert "success" in result
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool.requests.post")
|
||||
def test_run_api_error(self, mock_post):
|
||||
schema = {
|
||||
"function": {
|
||||
"name": "test_action",
|
||||
"description": "Test action",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {"type": "string", "description": "Message"}
|
||||
},
|
||||
"required": ["message"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tool = CrewAIPlatformActionTool(
|
||||
description="Test tool",
|
||||
action_name="test_action",
|
||||
action_schema=schema
|
||||
)
|
||||
|
||||
mock_response = Mock()
|
||||
mock_response.ok = False
|
||||
mock_response.json.return_value = {"error": {"message": "Invalid request"}}
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
result = tool._run(message="test message")
|
||||
|
||||
assert "API request failed" in result
|
||||
assert "Invalid request" in result
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool.requests.post")
|
||||
def test_run_exception(self, mock_post):
|
||||
schema = {
|
||||
"function": {
|
||||
"name": "test_action",
|
||||
"description": "Test action",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {"type": "string", "description": "Message"}
|
||||
},
|
||||
"required": ["message"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tool = CrewAIPlatformActionTool(
|
||||
description="Test tool",
|
||||
action_name="test_action",
|
||||
action_schema=schema
|
||||
)
|
||||
|
||||
mock_post.side_effect = Exception("Network error")
|
||||
|
||||
result = tool._run(message="test message")
|
||||
|
||||
assert "Error executing action test_action: Network error" in result
|
||||
|
||||
def test_run_without_token(self):
|
||||
schema = {
|
||||
"function": {
|
||||
"name": "test_action",
|
||||
"description": "Test action",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {"type": "string", "description": "Message"}
|
||||
},
|
||||
"required": ["message"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tool = CrewAIPlatformActionTool(
|
||||
description="Test tool",
|
||||
action_name="test_action",
|
||||
action_schema=schema
|
||||
)
|
||||
|
||||
with patch.dict("os.environ", {}, clear=True):
|
||||
result = tool._run(message="test message")
|
||||
assert "Error executing action test_action:" in result
|
||||
assert "No platform integration token found" in result
|
||||
@@ -0,0 +1,223 @@
|
||||
import unittest
|
||||
from unittest.mock import patch, Mock
|
||||
import pytest
|
||||
from crewai_tools.tools.crewai_platform_tools import CrewaiPlatformToolBuilder, CrewAIPlatformActionTool
|
||||
|
||||
|
||||
class TestCrewaiPlatformToolBuilder(unittest.TestCase):
|
||||
@pytest.fixture
|
||||
def platform_tool_builder(self):
|
||||
"""Create a CrewaiPlatformToolBuilder instance for testing"""
|
||||
return CrewaiPlatformToolBuilder(apps=["github", "slack"])
|
||||
|
||||
@pytest.fixture
|
||||
def mock_api_response(self):
|
||||
return {
|
||||
"actions": {
|
||||
"github": [
|
||||
{
|
||||
"name": "create_issue",
|
||||
"description": "Create a GitHub issue",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string", "description": "Issue title"},
|
||||
"body": {"type": "string", "description": "Issue body"}
|
||||
},
|
||||
"required": ["title"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"slack": [
|
||||
{
|
||||
"name": "send_message",
|
||||
"description": "Send a Slack message",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {"type": "string", "description": "Channel name"},
|
||||
"text": {"type": "string", "description": "Message text"}
|
||||
},
|
||||
"required": ["channel", "text"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get")
|
||||
def test_fetch_actions_success(self, mock_get):
|
||||
mock_api_response = {
|
||||
"actions": {
|
||||
"github": [
|
||||
{
|
||||
"name": "create_issue",
|
||||
"description": "Create a GitHub issue",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string", "description": "Issue title"}
|
||||
},
|
||||
"required": ["title"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
builder = CrewaiPlatformToolBuilder(apps=["github", "slack/send_message"])
|
||||
|
||||
mock_response = Mock()
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.return_value = mock_api_response
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
builder._fetch_actions()
|
||||
|
||||
mock_get.assert_called_once()
|
||||
args, kwargs = mock_get.call_args
|
||||
|
||||
assert "/actions" in args[0]
|
||||
assert kwargs["headers"]["Authorization"] == "Bearer test_token"
|
||||
assert kwargs["params"]["apps"] == "github,slack/send_message"
|
||||
|
||||
assert "create_issue" in builder._actions_schema
|
||||
assert builder._actions_schema["create_issue"]["function"]["name"] == "create_issue"
|
||||
|
||||
def test_fetch_actions_no_token(self):
|
||||
builder = CrewaiPlatformToolBuilder(apps=["github"])
|
||||
|
||||
with patch.dict("os.environ", {}, clear=True):
|
||||
with self.assertRaises(ValueError) as context:
|
||||
builder._fetch_actions()
|
||||
assert "No platform integration token found" in str(context.exception)
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get")
|
||||
def test_create_tools(self, mock_get):
|
||||
mock_api_response = {
|
||||
"actions": {
|
||||
"github": [
|
||||
{
|
||||
"name": "create_issue",
|
||||
"description": "Create a GitHub issue",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string", "description": "Issue title"}
|
||||
},
|
||||
"required": ["title"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"slack": [
|
||||
{
|
||||
"name": "send_message",
|
||||
"description": "Send a Slack message",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {"type": "string", "description": "Channel name"}
|
||||
},
|
||||
"required": ["channel"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
builder = CrewaiPlatformToolBuilder(apps=["github", "slack"])
|
||||
|
||||
mock_response = Mock()
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.return_value = mock_api_response
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
tools = builder.tools()
|
||||
|
||||
assert len(tools) == 2
|
||||
assert all(isinstance(tool, CrewAIPlatformActionTool) for tool in tools)
|
||||
|
||||
tool_names = [tool.action_name for tool in tools]
|
||||
assert "create_issue" in tool_names
|
||||
assert "send_message" in tool_names
|
||||
|
||||
github_tool = next((t for t in tools if t.action_name == "create_issue"), None)
|
||||
slack_tool = next((t for t in tools if t.action_name == "send_message"), None)
|
||||
|
||||
assert github_tool is not None
|
||||
assert slack_tool is not None
|
||||
assert "Create a GitHub issue" in github_tool.description
|
||||
assert "Send a Slack message" in slack_tool.description
|
||||
|
||||
def test_tools_caching(self):
|
||||
builder = CrewaiPlatformToolBuilder(apps=["github"])
|
||||
|
||||
cached_tools = []
|
||||
|
||||
def mock_create_tools():
|
||||
builder._tools = cached_tools
|
||||
|
||||
with patch.object(builder, '_fetch_actions') as mock_fetch, \
|
||||
patch.object(builder, '_create_tools', side_effect=mock_create_tools) as mock_create:
|
||||
|
||||
tools1 = builder.tools()
|
||||
assert mock_fetch.call_count == 1
|
||||
assert mock_create.call_count == 1
|
||||
|
||||
tools2 = builder.tools()
|
||||
assert mock_fetch.call_count == 1
|
||||
assert mock_create.call_count == 1
|
||||
|
||||
assert tools1 is tools2
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
def test_empty_apps_list(self):
|
||||
builder = CrewaiPlatformToolBuilder(apps=[])
|
||||
|
||||
with patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get") as mock_get:
|
||||
mock_response = Mock()
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.return_value = {"actions": {}}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
tools = builder.tools()
|
||||
|
||||
assert isinstance(tools, list)
|
||||
assert len(tools) == 0
|
||||
|
||||
_, kwargs = mock_get.call_args
|
||||
assert kwargs["params"]["apps"] == ""
|
||||
|
||||
def test_detailed_description_generation(self):
|
||||
builder = CrewaiPlatformToolBuilder(apps=["test"])
|
||||
|
||||
complex_schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"simple_string": {"type": "string", "description": "A simple string"},
|
||||
"nested_object": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"inner_prop": {"type": "integer", "description": "Inner property"}
|
||||
},
|
||||
"description": "Nested object"
|
||||
},
|
||||
"array_prop": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Array of strings"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
descriptions = builder._generate_detailed_description(complex_schema)
|
||||
|
||||
assert isinstance(descriptions, list)
|
||||
assert len(descriptions) > 0
|
||||
|
||||
description_text = "\n".join(descriptions)
|
||||
assert "simple_string" in description_text
|
||||
assert "nested_object" in description_text
|
||||
assert "array_prop" in description_text
|
||||
@@ -0,0 +1,95 @@
|
||||
import unittest
|
||||
from unittest.mock import patch, Mock
|
||||
from crewai_tools.tools.crewai_platform_tools import CrewaiPlatformTools
|
||||
|
||||
|
||||
class TestCrewaiPlatformTools(unittest.TestCase):
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get")
|
||||
def test_crewai_platform_tools_basic(self, mock_get):
|
||||
mock_response = Mock()
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.return_value = {"actions": {"github": []}}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
tools = CrewaiPlatformTools(apps=["github"])
|
||||
assert tools is not None
|
||||
assert isinstance(tools, list)
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get")
|
||||
def test_crewai_platform_tools_multiple_apps(self, mock_get):
|
||||
mock_response = Mock()
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.return_value = {
|
||||
"actions": {
|
||||
"github": [
|
||||
{
|
||||
"name": "create_issue",
|
||||
"description": "Create a GitHub issue",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string", "description": "Issue title"},
|
||||
"body": {"type": "string", "description": "Issue body"}
|
||||
},
|
||||
"required": ["title"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"slack": [
|
||||
{
|
||||
"name": "send_message",
|
||||
"description": "Send a Slack message",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {"type": "string", "description": "Channel to send to"},
|
||||
"text": {"type": "string", "description": "Message text"}
|
||||
},
|
||||
"required": ["channel", "text"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
tools = CrewaiPlatformTools(apps=["github", "slack"])
|
||||
assert tools is not None
|
||||
assert isinstance(tools, list)
|
||||
assert len(tools) == 2
|
||||
|
||||
mock_get.assert_called_once()
|
||||
args, kwargs = mock_get.call_args
|
||||
assert "apps=github,slack" in args[0] or kwargs.get("params", {}).get("apps") == "github,slack"
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
def test_crewai_platform_tools_empty_apps(self):
|
||||
with patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get") as mock_get:
|
||||
mock_response = Mock()
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.return_value = {"actions": {}}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
tools = CrewaiPlatformTools(apps=[])
|
||||
assert tools is not None
|
||||
assert isinstance(tools, list)
|
||||
assert len(tools) == 0
|
||||
|
||||
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"})
|
||||
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get")
|
||||
def test_crewai_platform_tools_api_error_handling(self, mock_get):
|
||||
mock_get.side_effect = Exception("API Error")
|
||||
|
||||
tools = CrewaiPlatformTools(apps=["github"])
|
||||
assert tools is not None
|
||||
assert isinstance(tools, list)
|
||||
assert len(tools) == 0
|
||||
|
||||
def test_crewai_platform_tools_no_token(self):
|
||||
with patch.dict("os.environ", {}, clear=True):
|
||||
with self.assertRaises(ValueError) as context:
|
||||
CrewaiPlatformTools(apps=["github"])
|
||||
assert "No platform integration token found" in str(context.exception)
|
||||
Reference in New Issue
Block a user