From 999bee834403cfc70868350d1610e07ab910264c Mon Sep 17 00:00:00 2001 From: Vinicius Brasil Date: Thu, 16 Jul 2026 12:12:29 -0300 Subject: [PATCH] Add organization ID param to PlusAPI client (#6561) This commit adds the organization ID parameter to the PlusAPI client, in addition to the settings file. This allows for settings the organization programmatically. --- lib/crewai-core/src/crewai_core/plus_api.py | 17 ++-- lib/crewai/tests/cli/test_plus_api.py | 92 +++++++++++++++++++++ 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/lib/crewai-core/src/crewai_core/plus_api.py b/lib/crewai-core/src/crewai_core/plus_api.py index d32e95856..4ed5e640d 100644 --- a/lib/crewai-core/src/crewai_core/plus_api.py +++ b/lib/crewai-core/src/crewai_core/plus_api.py @@ -149,7 +149,13 @@ class PlusAPI: EPHEMERAL_TRACING_RESOURCE: Final = "/crewai_plus/api/v1/tracing/ephemeral" INTEGRATIONS_RESOURCE: Final = "/crewai_plus/api/v1/integrations" - def __init__(self, api_key: str | None = None) -> None: + def __init__( + self, + api_key: str | None = None, + *, + base_url: str | None = None, + organization_id: str | None = None, + ) -> None: version = get_crewai_version() self.api_key = api_key self.headers: Headers = { @@ -161,12 +167,13 @@ class PlusAPI: self.headers["Authorization"] = f"Bearer {api_key}" settings = Settings() - if settings.org_uuid: - self.headers["X-Crewai-Organization-Id"] = settings.org_uuid + if organization_id := organization_id or settings.org_uuid: + self.headers["X-Crewai-Organization-Id"] = organization_id self.base_url = ( - os.getenv("CREWAI_PLUS_URL") - or str(settings.enterprise_base_url) + base_url + or os.getenv("CREWAI_PLUS_URL") + or settings.enterprise_base_url or DEFAULT_CREWAI_ENTERPRISE_URL ) diff --git a/lib/crewai/tests/cli/test_plus_api.py b/lib/crewai/tests/cli/test_plus_api.py index 7b86880e8..5ba215aa5 100644 --- a/lib/crewai/tests/cli/test_plus_api.py +++ b/lib/crewai/tests/cli/test_plus_api.py @@ -1,7 +1,10 @@ import os import unittest +from types import SimpleNamespace from unittest.mock import ANY, MagicMock, patch +import pytest + from crewai.plus_api import PlusAPI @@ -393,6 +396,45 @@ class TestPlusAPI(unittest.TestCase): "https://custom-url-from-env.com", ) + @patch.dict(os.environ, {"CREWAI_PLUS_URL": "https://url-from-env.com"}) + def test_explicit_target_takes_precedence(self): + api = PlusAPI( + "test_key", + base_url="https://explicit-url.com", + organization_id="explicit-org-id", + ) + + self.assertEqual(api.base_url, "https://explicit-url.com") + self.assertEqual( + api.headers["X-Crewai-Organization-Id"], "explicit-org-id" + ) + + @patch("crewai_core.plus_api.Settings") + def test_explicit_base_url_uses_organization_from_settings( + self, mock_settings_class + ): + mock_settings_class.return_value.org_uuid = "settings-org-id" + + api = PlusAPI("test_key", base_url="https://explicit-url.com") + + self.assertEqual(api.base_url, "https://explicit-url.com") + self.assertEqual(api.headers["X-Crewai-Organization-Id"], "settings-org-id") + + @patch("crewai_core.plus_api.Settings") + @patch.dict(os.environ, {"CREWAI_PLUS_URL": ""}) + def test_explicit_organization_uses_base_url_from_settings( + self, mock_settings_class + ): + mock_settings_class.return_value.enterprise_base_url = ( + "https://url-from-settings.com" + ) + + api = PlusAPI("test_key", organization_id="explicit-org-id") + + self.assertEqual(api.base_url, "https://url-from-settings.com") + self.assertEqual( + api.headers["X-Crewai-Organization-Id"], "explicit-org-id" + ) @patch("crewai_core.plus_api.PlusAPI._make_request") def test_get_agent(mock_make_request): @@ -430,3 +472,53 @@ def test_get_agent_with_org_uuid(mock_settings_class, mock_make_request): assert "X-Crewai-Organization-Id" in api.headers assert api.headers["X-Crewai-Organization-Id"] == org_uuid assert response == mock_response + + +@pytest.mark.parametrize( + ("target", "other_target"), + ( + ( + ("https://first.example.com", "first-org"), + ("https://second.example.com", "second-org"), + ), + ( + ("https://second.example.com", "second-org"), + ("https://first.example.com", "first-org"), + ), + ), +) +def test_clients_keep_targets_independent(target, other_target): + base_url, organization_id = target + api = PlusAPI( + "test_key", + base_url=base_url, + organization_id=organization_id, + ) + other_base_url, other_organization_id = other_target + PlusAPI( + "test_key", + base_url=other_base_url, + organization_id=other_organization_id, + ) + + assert (api.base_url, api.headers["X-Crewai-Organization-Id"]) == target + + +@patch("crewai_core.plus_api.Settings") +@patch.dict(os.environ, {"CREWAI_PLUS_URL": ""}) +def test_default_target_uses_single_settings_snapshot(mock_settings_class): + mock_settings_class.side_effect = ( + SimpleNamespace( + org_uuid="first-org", + enterprise_base_url="https://first.example.com", + ), + SimpleNamespace( + org_uuid="second-org", + enterprise_base_url="https://second.example.com", + ), + ) + + api = PlusAPI("test_key") + + assert api.base_url == "https://first.example.com" + assert api.headers["X-Crewai-Organization-Id"] == "first-org"