diff --git a/src/crewai/cli/deploy/api.py b/src/crewai/cli/deploy/api.py index a7807dda4..4a8954cdc 100644 --- a/src/crewai/cli/deploy/api.py +++ b/src/crewai/cli/deploy/api.py @@ -1,66 +1,56 @@ -from os import getenv - import requests - -from crewai.cli.deploy.utils import get_crewai_version +from crewai.cli.plus_api import PlusAPI -class CrewAPI: +class CrewAPI(PlusAPI): """ - CrewAPI class to interact with the crewAI+ API. + CrewAPI class to interact with the Crew resource in CrewAI+ API. """ - def __init__(self, api_key: str) -> None: - self.api_key = api_key - self.headers = { - "Authorization": f"Bearer {api_key}", - "Content-Type": "application/json", - "User-Agent": f"CrewAI-CLI/{get_crewai_version()}", - } - self.base_url = getenv( - "CREWAI_BASE_URL", "https://app.crewai.com/crewai_plus/api/v1/crews" - ) - - def _make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response: - url = f"{self.base_url}/{endpoint}" - return requests.request(method, url, headers=self.headers, **kwargs) + RESOURCE = "/crewai_plus/api/v1/crews" # Deploy def deploy_by_name(self, project_name: str) -> requests.Response: - return self._make_request("POST", f"by-name/{project_name}/deploy") + return self._make_request( + "POST", f"{self.RESOURCE}/by-name/{project_name}/deploy" + ) def deploy_by_uuid(self, uuid: str) -> requests.Response: - return self._make_request("POST", f"{uuid}/deploy") + return self._make_request("POST", f"{self.RESOURCE}/{uuid}/deploy") # Status def status_by_name(self, project_name: str) -> requests.Response: - return self._make_request("GET", f"by-name/{project_name}/status") + return self._make_request( + "GET", f"{self.RESOURCE}/by-name/{project_name}/status" + ) def status_by_uuid(self, uuid: str) -> requests.Response: - return self._make_request("GET", f"{uuid}/status") + return self._make_request("GET", f"{self.RESOURCE}/{uuid}/status") # Logs def logs_by_name( self, project_name: str, log_type: str = "deployment" ) -> requests.Response: - return self._make_request("GET", f"by-name/{project_name}/logs/{log_type}") + return self._make_request( + "GET", f"{self.RESOURCE}/by-name/{project_name}/logs/{log_type}" + ) def logs_by_uuid( self, uuid: str, log_type: str = "deployment" ) -> requests.Response: - return self._make_request("GET", f"{uuid}/logs/{log_type}") + return self._make_request("GET", f"{self.RESOURCE}/{uuid}/logs/{log_type}") # Delete def delete_by_name(self, project_name: str) -> requests.Response: - return self._make_request("DELETE", f"by-name/{project_name}") + return self._make_request("DELETE", f"{self.RESOURCE}/by-name/{project_name}") def delete_by_uuid(self, uuid: str) -> requests.Response: - return self._make_request("DELETE", f"{uuid}") + return self._make_request("DELETE", f"{self.RESOURCE}/{uuid}") # List def list_crews(self) -> requests.Response: - return self._make_request("GET", "") + return self._make_request("GET", self.RESOURCE) # Create def create_crew(self, payload) -> requests.Response: - return self._make_request("POST", "", json=payload) + return self._make_request("POST", self.RESOURCE, json=payload) diff --git a/src/crewai/cli/plus_api.py b/src/crewai/cli/plus_api.py new file mode 100644 index 000000000..22ccc8352 --- /dev/null +++ b/src/crewai/cli/plus_api.py @@ -0,0 +1,23 @@ +import requests +from os import getenv +from crewai.cli.deploy.utils import get_crewai_version + + +class PlusAPI: + """ + This class exposes methods for working with the CrewAI+ API. + """ + + def __init__(self, api_key: str) -> None: + self.api_key = api_key + self.headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + "User-Agent": f"CrewAI-CLI/{get_crewai_version()}", + "X-Crewai-Version": get_crewai_version(), + } + self.base_url = getenv("CREWAI_BASE_URL", "https://app.crewai.com") + + def _make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response: + url = f"{self.base_url}/{endpoint}" + return requests.request(method, url, headers=self.headers, **kwargs) diff --git a/src/crewai/cli/tools/__init__.py b/src/crewai/cli/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/crewai/cli/tools/api.py b/src/crewai/cli/tools/api.py new file mode 100644 index 000000000..c93fdb9c0 --- /dev/null +++ b/src/crewai/cli/tools/api.py @@ -0,0 +1,26 @@ +from typing import Optional +from crewai.cli.plus_api import PlusAPI + + +class ToolsAPI(PlusAPI): + RESOURCE = "/crewai_plus/api/v1/tools" + + def get(self, handle: str): + return self._make_request("GET", f"{self.RESOURCE}/{handle}") + + def publish( + self, + handle: str, + public: bool, + version: str, + description: Optional[str], + encoded_file: str, + ): + params = { + "handle": handle, + "public": public, + "version": version, + "file": encoded_file, + "description": description, + } + return self._make_request("POST", f"{self.RESOURCE}", json=params) diff --git a/tests/cli/deploy/test_api.py b/tests/cli/deploy/test_api.py index 616a9ff3d..77a5ff84d 100644 --- a/tests/cli/deploy/test_api.py +++ b/tests/cli/deploy/test_api.py @@ -17,11 +17,12 @@ class TestCrewAPI(unittest.TestCase): { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json", - "User-Agent": "CrewAI-CLI/no-version-found" + "User-Agent": "CrewAI-CLI/no-version-found", + "X-Crewai-Version": "no-version-found", }, ) - @patch("crewai.cli.deploy.api.requests.request") + @patch("crewai.cli.plus_api.requests.request") def test_make_request(self, mock_request): mock_response = MagicMock() mock_request.return_value = mock_response @@ -33,66 +34,84 @@ class TestCrewAPI(unittest.TestCase): ) self.assertEqual(response, mock_response) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_deploy_by_name(self, mock_make_request): self.api.deploy_by_name("test_project") - mock_make_request.assert_called_once_with("POST", "by-name/test_project/deploy") + mock_make_request.assert_called_once_with( + "POST", "/crewai_plus/api/v1/crews/by-name/test_project/deploy" + ) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_deploy_by_uuid(self, mock_make_request): self.api.deploy_by_uuid("test_uuid") - mock_make_request.assert_called_once_with("POST", "test_uuid/deploy") + mock_make_request.assert_called_once_with( + "POST", "/crewai_plus/api/v1/crews/test_uuid/deploy" + ) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_status_by_name(self, mock_make_request): self.api.status_by_name("test_project") - mock_make_request.assert_called_once_with("GET", "by-name/test_project/status") + mock_make_request.assert_called_once_with( + "GET", "/crewai_plus/api/v1/crews/by-name/test_project/status" + ) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_status_by_uuid(self, mock_make_request): self.api.status_by_uuid("test_uuid") - mock_make_request.assert_called_once_with("GET", "test_uuid/status") + mock_make_request.assert_called_once_with( + "GET", "/crewai_plus/api/v1/crews/test_uuid/status" + ) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_logs_by_name(self, mock_make_request): self.api.logs_by_name("test_project") mock_make_request.assert_called_once_with( - "GET", "by-name/test_project/logs/deployment" + "GET", "/crewai_plus/api/v1/crews/by-name/test_project/logs/deployment" ) self.api.logs_by_name("test_project", "custom_log") mock_make_request.assert_called_with( - "GET", "by-name/test_project/logs/custom_log" + "GET", "/crewai_plus/api/v1/crews/by-name/test_project/logs/custom_log" ) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_logs_by_uuid(self, mock_make_request): self.api.logs_by_uuid("test_uuid") - mock_make_request.assert_called_once_with("GET", "test_uuid/logs/deployment") + mock_make_request.assert_called_once_with( + "GET", "/crewai_plus/api/v1/crews/test_uuid/logs/deployment" + ) self.api.logs_by_uuid("test_uuid", "custom_log") - mock_make_request.assert_called_with("GET", "test_uuid/logs/custom_log") + mock_make_request.assert_called_with( + "GET", "/crewai_plus/api/v1/crews/test_uuid/logs/custom_log" + ) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_delete_by_name(self, mock_make_request): self.api.delete_by_name("test_project") - mock_make_request.assert_called_once_with("DELETE", "by-name/test_project") + mock_make_request.assert_called_once_with( + "DELETE", "/crewai_plus/api/v1/crews/by-name/test_project" + ) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_delete_by_uuid(self, mock_make_request): self.api.delete_by_uuid("test_uuid") - mock_make_request.assert_called_once_with("DELETE", "test_uuid") + mock_make_request.assert_called_once_with( + "DELETE", "/crewai_plus/api/v1/crews/test_uuid" + ) - @patch("crewai.cli.deploy.api.CrewAPI._make_request") + @patch("crewai.cli.plus_api.PlusAPI._make_request") def test_list_crews(self, mock_make_request): self.api.list_crews() - mock_make_request.assert_called_once_with("GET", "") + mock_make_request.assert_called_once_with("GET", "/crewai_plus/api/v1/crews") @patch("crewai.cli.deploy.api.CrewAPI._make_request") def test_create_crew(self, mock_make_request): payload = {"name": "test_crew"} self.api.create_crew(payload) - mock_make_request.assert_called_once_with("POST", "", json=payload) + mock_make_request.assert_called_once_with( + "POST", "/crewai_plus/api/v1/crews", json=payload + ) @patch.dict(environ, {"CREWAI_BASE_URL": "https://custom-url.com/api"}) def test_custom_base_url(self): diff --git a/tests/cli/tools/__init__.py b/tests/cli/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cli/tools/test_api.py b/tests/cli/tools/test_api.py new file mode 100644 index 000000000..17c100678 --- /dev/null +++ b/tests/cli/tools/test_api.py @@ -0,0 +1,69 @@ +import unittest +from unittest.mock import MagicMock, patch +from crewai.cli.tools.api import ToolsAPI + + +class TestToolsAPI(unittest.TestCase): + def setUp(self): + self.api_key = "test_api_key" + self.api = ToolsAPI(self.api_key) + + @patch("crewai.cli.plus_api.PlusAPI._make_request") + def test_get_tool(self, mock_make_request): + mock_response = MagicMock() + mock_make_request.return_value = mock_response + + response = self.api.get("test_tool_handle") + + mock_make_request.assert_called_once_with( + "GET", "/crewai_plus/api/v1/tools/test_tool_handle" + ) + self.assertEqual(response, mock_response) + + @patch("crewai.cli.plus_api.PlusAPI._make_request") + def test_publish_tool(self, mock_make_request): + mock_response = MagicMock() + mock_make_request.return_value = mock_response + handle = "test_tool_handle" + public = True + version = "1.0.0" + description = "Test tool description" + encoded_file = "encoded_test_file" + + response = self.api.publish(handle, public, version, description, encoded_file) + + params = { + "handle": handle, + "public": public, + "version": version, + "file": encoded_file, + "description": description, + } + mock_make_request.assert_called_once_with( + "POST", "/crewai_plus/api/v1/tools", json=params + ) + self.assertEqual(response, mock_response) + + @patch("crewai.cli.plus_api.PlusAPI._make_request") + def test_publish_tool_without_description(self, mock_make_request): + mock_response = MagicMock() + mock_make_request.return_value = mock_response + handle = "test_tool_handle" + public = False + version = "2.0.0" + description = None + encoded_file = "encoded_test_file" + + response = self.api.publish(handle, public, version, description, encoded_file) + + params = { + "handle": handle, + "public": public, + "version": version, + "file": encoded_file, + "description": description, + } + mock_make_request.assert_called_once_with( + "POST", "/crewai_plus/api/v1/tools", json=params + ) + self.assertEqual(response, mock_response)