Skip to content

Commit fe288db

Browse files
authored
Resolving some connection issues (#4129)
* fix: use CREWAI_PLUS_URL env var in precedence over PlusAPI configured value * feat: bypass TLS certificate verification when calling platform * test: fix test
1 parent dc63bc2 commit fe288db

File tree

6 files changed

+228
-13
lines changed

6 files changed

+228
-13
lines changed

lib/crewai-tools/src/crewai_tools/tools/crewai_platform_tools/crewai_platform_action_tool.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Crewai Enterprise Tools."""
2-
2+
import os
33
import json
44
import re
55
from typing import Any, Optional, Union, cast, get_origin
@@ -432,7 +432,11 @@ def _run(self, **kwargs) -> str:
432432
payload = cleaned_kwargs
433433

434434
response = requests.post(
435-
url=api_url, headers=headers, json=payload, timeout=60
435+
url=api_url,
436+
headers=headers,
437+
json=payload,
438+
timeout=60,
439+
verify=os.environ.get("CREWAI_FACTORY", "false").lower() != "true",
436440
)
437441

438442
data = response.json()

lib/crewai-tools/src/crewai_tools/tools/crewai_platform_tools/crewai_platform_tool_builder.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import Any
2-
2+
import os
33
from crewai.tools import BaseTool
44
import requests
55

@@ -37,6 +37,7 @@ def _fetch_actions(self):
3737
headers=headers,
3838
timeout=30,
3939
params={"apps": ",".join(self._apps)},
40+
verify=os.environ.get("CREWAI_FACTORY", "false").lower() != "true",
4041
)
4142
response.raise_for_status()
4243
except Exception:

lib/crewai-tools/tests/tools/crewai_platform_tools/test_crewai_platform_action_tool.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from typing import Union, get_args, get_origin
2+
from unittest.mock import patch, Mock
3+
import os
24

35
from crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool import (
46
CrewAIPlatformActionTool,
@@ -249,3 +251,109 @@ def test_allof_mixed_types(self):
249251
result_type = tool._process_schema_type(test_schema, "TestFieldAllOfMixed")
250252

251253
assert result_type is str
254+
255+
class TestCrewAIPlatformActionToolVerify:
256+
"""Test suite for SSL verification behavior based on CREWAI_FACTORY environment variable"""
257+
258+
def setup_method(self):
259+
self.action_schema = {
260+
"function": {
261+
"name": "test_action",
262+
"parameters": {
263+
"properties": {
264+
"test_param": {
265+
"type": "string",
266+
"description": "Test parameter"
267+
}
268+
},
269+
"required": []
270+
}
271+
}
272+
}
273+
274+
def create_test_tool(self):
275+
return CrewAIPlatformActionTool(
276+
description="Test action tool",
277+
action_name="test_action",
278+
action_schema=self.action_schema
279+
)
280+
281+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"}, clear=True)
282+
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool.requests.post")
283+
def test_run_with_ssl_verification_default(self, mock_post):
284+
"""Test that _run uses SSL verification by default when CREWAI_FACTORY is not set"""
285+
mock_response = Mock()
286+
mock_response.ok = True
287+
mock_response.json.return_value = {"result": "success"}
288+
mock_post.return_value = mock_response
289+
290+
tool = self.create_test_tool()
291+
tool._run(test_param="test_value")
292+
293+
mock_post.assert_called_once()
294+
call_args = mock_post.call_args
295+
assert call_args.kwargs["verify"] is True
296+
297+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token", "CREWAI_FACTORY": "false"}, clear=True)
298+
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool.requests.post")
299+
def test_run_with_ssl_verification_factory_false(self, mock_post):
300+
"""Test that _run uses SSL verification when CREWAI_FACTORY is 'false'"""
301+
mock_response = Mock()
302+
mock_response.ok = True
303+
mock_response.json.return_value = {"result": "success"}
304+
mock_post.return_value = mock_response
305+
306+
tool = self.create_test_tool()
307+
tool._run(test_param="test_value")
308+
309+
mock_post.assert_called_once()
310+
call_args = mock_post.call_args
311+
assert call_args.kwargs["verify"] is True
312+
313+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token", "CREWAI_FACTORY": "FALSE"}, clear=True)
314+
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool.requests.post")
315+
def test_run_with_ssl_verification_factory_false_uppercase(self, mock_post):
316+
"""Test that _run uses SSL verification when CREWAI_FACTORY is 'FALSE' (case-insensitive)"""
317+
mock_response = Mock()
318+
mock_response.ok = True
319+
mock_response.json.return_value = {"result": "success"}
320+
mock_post.return_value = mock_response
321+
322+
tool = self.create_test_tool()
323+
tool._run(test_param="test_value")
324+
325+
mock_post.assert_called_once()
326+
call_args = mock_post.call_args
327+
assert call_args.kwargs["verify"] is True
328+
329+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token", "CREWAI_FACTORY": "true"}, clear=True)
330+
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool.requests.post")
331+
def test_run_without_ssl_verification_factory_true(self, mock_post):
332+
"""Test that _run disables SSL verification when CREWAI_FACTORY is 'true'"""
333+
mock_response = Mock()
334+
mock_response.ok = True
335+
mock_response.json.return_value = {"result": "success"}
336+
mock_post.return_value = mock_response
337+
338+
tool = self.create_test_tool()
339+
tool._run(test_param="test_value")
340+
341+
mock_post.assert_called_once()
342+
call_args = mock_post.call_args
343+
assert call_args.kwargs["verify"] is False
344+
345+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token", "CREWAI_FACTORY": "TRUE"}, clear=True)
346+
@patch("crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool.requests.post")
347+
def test_run_without_ssl_verification_factory_true_uppercase(self, mock_post):
348+
"""Test that _run disables SSL verification when CREWAI_FACTORY is 'TRUE' (case-insensitive)"""
349+
mock_response = Mock()
350+
mock_response.ok = True
351+
mock_response.json.return_value = {"result": "success"}
352+
mock_post.return_value = mock_response
353+
354+
tool = self.create_test_tool()
355+
tool._run(test_param="test_value")
356+
357+
mock_post.assert_called_once()
358+
call_args = mock_post.call_args
359+
assert call_args.kwargs["verify"] is False

lib/crewai-tools/tests/tools/crewai_platform_tools/test_crewai_platform_tool_builder.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,98 @@ def test_detailed_description_generation(self):
258258
assert "simple_string" in description_text
259259
assert "nested_object" in description_text
260260
assert "array_prop" in description_text
261+
262+
263+
264+
class TestCrewaiPlatformToolBuilderVerify(unittest.TestCase):
265+
"""Test suite for SSL verification behavior in CrewaiPlatformToolBuilder"""
266+
267+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token"}, clear=True)
268+
@patch(
269+
"crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get"
270+
)
271+
def test_fetch_actions_with_ssl_verification_default(self, mock_get):
272+
"""Test that _fetch_actions uses SSL verification by default when CREWAI_FACTORY is not set"""
273+
mock_response = Mock()
274+
mock_response.raise_for_status.return_value = None
275+
mock_response.json.return_value = {"actions": {}}
276+
mock_get.return_value = mock_response
277+
278+
builder = CrewaiPlatformToolBuilder(apps=["github"])
279+
builder._fetch_actions()
280+
281+
mock_get.assert_called_once()
282+
call_args = mock_get.call_args
283+
assert call_args.kwargs["verify"] is True
284+
285+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token", "CREWAI_FACTORY": "false"}, clear=True)
286+
@patch(
287+
"crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get"
288+
)
289+
def test_fetch_actions_with_ssl_verification_factory_false(self, mock_get):
290+
"""Test that _fetch_actions uses SSL verification when CREWAI_FACTORY is 'false'"""
291+
mock_response = Mock()
292+
mock_response.raise_for_status.return_value = None
293+
mock_response.json.return_value = {"actions": {}}
294+
mock_get.return_value = mock_response
295+
296+
builder = CrewaiPlatformToolBuilder(apps=["github"])
297+
builder._fetch_actions()
298+
299+
mock_get.assert_called_once()
300+
call_args = mock_get.call_args
301+
assert call_args.kwargs["verify"] is True
302+
303+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token", "CREWAI_FACTORY": "FALSE"}, clear=True)
304+
@patch(
305+
"crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get"
306+
)
307+
def test_fetch_actions_with_ssl_verification_factory_false_uppercase(self, mock_get):
308+
"""Test that _fetch_actions uses SSL verification when CREWAI_FACTORY is 'FALSE' (case-insensitive)"""
309+
mock_response = Mock()
310+
mock_response.raise_for_status.return_value = None
311+
mock_response.json.return_value = {"actions": {}}
312+
mock_get.return_value = mock_response
313+
314+
builder = CrewaiPlatformToolBuilder(apps=["github"])
315+
builder._fetch_actions()
316+
317+
mock_get.assert_called_once()
318+
call_args = mock_get.call_args
319+
assert call_args.kwargs["verify"] is True
320+
321+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token", "CREWAI_FACTORY": "true"}, clear=True)
322+
@patch(
323+
"crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get"
324+
)
325+
def test_fetch_actions_without_ssl_verification_factory_true(self, mock_get):
326+
"""Test that _fetch_actions disables SSL verification when CREWAI_FACTORY is 'true'"""
327+
mock_response = Mock()
328+
mock_response.raise_for_status.return_value = None
329+
mock_response.json.return_value = {"actions": {}}
330+
mock_get.return_value = mock_response
331+
332+
builder = CrewaiPlatformToolBuilder(apps=["github"])
333+
builder._fetch_actions()
334+
335+
mock_get.assert_called_once()
336+
call_args = mock_get.call_args
337+
assert call_args.kwargs["verify"] is False
338+
339+
@patch.dict("os.environ", {"CREWAI_PLATFORM_INTEGRATION_TOKEN": "test_token", "CREWAI_FACTORY": "TRUE"}, clear=True)
340+
@patch(
341+
"crewai_tools.tools.crewai_platform_tools.crewai_platform_tool_builder.requests.get"
342+
)
343+
def test_fetch_actions_without_ssl_verification_factory_true_uppercase(self, mock_get):
344+
"""Test that _fetch_actions disables SSL verification when CREWAI_FACTORY is 'TRUE' (case-insensitive)"""
345+
mock_response = Mock()
346+
mock_response.raise_for_status.return_value = None
347+
mock_response.json.return_value = {"actions": {}}
348+
mock_get.return_value = mock_response
349+
350+
builder = CrewaiPlatformToolBuilder(apps=["github"])
351+
builder._fetch_actions()
352+
353+
mock_get.assert_called_once()
354+
call_args = mock_get.call_args
355+
assert call_args.kwargs["verify"] is False

lib/crewai/src/crewai/cli/plus_api.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any
22
from urllib.parse import urljoin
3-
3+
import os
44
import requests
55

66
from crewai.cli.config import Settings
@@ -33,9 +33,7 @@ def __init__(self, api_key: str) -> None:
3333
if settings.org_uuid:
3434
self.headers["X-Crewai-Organization-Id"] = settings.org_uuid
3535

36-
self.base_url = (
37-
str(settings.enterprise_base_url) or DEFAULT_CREWAI_ENTERPRISE_URL
38-
)
36+
self.base_url = os.getenv("CREWAI_PLUS_URL") or str(settings.enterprise_base_url) or DEFAULT_CREWAI_ENTERPRISE_URL
3937

4038
def _make_request(
4139
self, method: str, endpoint: str, **kwargs: Any

lib/crewai/tests/cli/test_plus_api.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import os
12
import unittest
23
from unittest.mock import ANY, MagicMock, patch
34

4-
from crewai.cli.constants import DEFAULT_CREWAI_ENTERPRISE_URL
55
from crewai.cli.plus_api import PlusAPI
66

77

@@ -35,7 +35,7 @@ def assert_request_with_org_id(
3535
):
3636
mock_make_request.assert_called_once_with(
3737
method,
38-
f"{DEFAULT_CREWAI_ENTERPRISE_URL}{endpoint}",
38+
f"{os.getenv('CREWAI_PLUS_URL')}{endpoint}",
3939
headers={
4040
"Authorization": ANY,
4141
"Content-Type": ANY,
@@ -53,7 +53,7 @@ def test_login_to_tool_repository_with_org_uuid(
5353
):
5454
mock_settings = MagicMock()
5555
mock_settings.org_uuid = self.org_uuid
56-
mock_settings.enterprise_base_url = DEFAULT_CREWAI_ENTERPRISE_URL
56+
mock_settings.enterprise_base_url = os.getenv('CREWAI_PLUS_URL')
5757
mock_settings_class.return_value = mock_settings
5858
# re-initialize Client
5959
self.api = PlusAPI(self.api_key)
@@ -84,7 +84,7 @@ def test_get_agent(self, mock_make_request):
8484
def test_get_agent_with_org_uuid(self, mock_make_request, mock_settings_class):
8585
mock_settings = MagicMock()
8686
mock_settings.org_uuid = self.org_uuid
87-
mock_settings.enterprise_base_url = DEFAULT_CREWAI_ENTERPRISE_URL
87+
mock_settings.enterprise_base_url = os.getenv('CREWAI_PLUS_URL')
8888
mock_settings_class.return_value = mock_settings
8989
# re-initialize Client
9090
self.api = PlusAPI(self.api_key)
@@ -115,7 +115,7 @@ def test_get_tool(self, mock_make_request):
115115
def test_get_tool_with_org_uuid(self, mock_make_request, mock_settings_class):
116116
mock_settings = MagicMock()
117117
mock_settings.org_uuid = self.org_uuid
118-
mock_settings.enterprise_base_url = DEFAULT_CREWAI_ENTERPRISE_URL
118+
mock_settings.enterprise_base_url = os.getenv('CREWAI_PLUS_URL')
119119
mock_settings_class.return_value = mock_settings
120120
# re-initialize Client
121121
self.api = PlusAPI(self.api_key)
@@ -163,7 +163,7 @@ def test_publish_tool(self, mock_make_request):
163163
def test_publish_tool_with_org_uuid(self, mock_make_request, mock_settings_class):
164164
mock_settings = MagicMock()
165165
mock_settings.org_uuid = self.org_uuid
166-
mock_settings.enterprise_base_url = DEFAULT_CREWAI_ENTERPRISE_URL
166+
mock_settings.enterprise_base_url = os.getenv('CREWAI_PLUS_URL')
167167
mock_settings_class.return_value = mock_settings
168168
# re-initialize Client
169169
self.api = PlusAPI(self.api_key)
@@ -320,6 +320,7 @@ def test_create_crew(self, mock_make_request):
320320
)
321321

322322
@patch("crewai.cli.plus_api.Settings")
323+
@patch.dict(os.environ, {"CREWAI_PLUS_URL": ""})
323324
def test_custom_base_url(self, mock_settings_class):
324325
mock_settings = MagicMock()
325326
mock_settings.enterprise_base_url = "https://custom-url.com/api"
@@ -329,3 +330,11 @@ def test_custom_base_url(self, mock_settings_class):
329330
custom_api.base_url,
330331
"https://custom-url.com/api",
331332
)
333+
334+
@patch.dict(os.environ, {"CREWAI_PLUS_URL": "https://custom-url-from-env.com"})
335+
def test_custom_base_url_from_env(self):
336+
custom_api = PlusAPI("test_key")
337+
self.assertEqual(
338+
custom_api.base_url,
339+
"https://custom-url-from-env.com",
340+
)

0 commit comments

Comments
 (0)