diff --git a/litellm/integrations/galileo.py b/litellm/integrations/galileo.py index 8fef90c24e0..9e2d64dc6a4 100644 --- a/litellm/integrations/galileo.py +++ b/litellm/integrations/galileo.py @@ -89,6 +89,51 @@ class GalileoObserve(CustomLogger): return bool(self.api_key) return bool(self.username and self.password) + async def async_health_check(self) -> dict: + try: + if not self.project_id: + return { + "status": "unhealthy", + "error_message": "GALILEO_PROJECT_ID environment variable not set", + } + + if not self.base_url: + return { + "status": "unhealthy", + "error_message": "GALILEO_BASE_URL environment variable not set", + } + + if self.use_v2_api: + if not self.api_key: + return { + "status": "unhealthy", + "error_message": "GALILEO_API_KEY environment variable not set", + } + elif not self.username or not self.password: + return { + "status": "unhealthy", + "error_message": ( + "GALILEO_USERNAME and GALILEO_PASSWORD environment " + "variables must be set" + ), + } + + if not await self._ensure_headers(): + return { + "status": "unhealthy", + "error_message": "Galileo authentication failed", + } + + return { + "status": "healthy", + "message": "Galileo credentials are configured properly", + } + except Exception as e: + return { + "status": "unhealthy", + "error_message": f"Galileo health check failed: {str(e)}", + } + async def async_set_galileo_headers(self) -> None: galileo_login_response = await self.async_httpx_handler.post( url=f"{self.base_url}/login", diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index c109f374993..6ef8bbc4006 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -129,6 +129,7 @@ services = Union[ "datadog_llm_observability", "generic_api", "arize", + "galileo", "sqs", ], str, @@ -206,6 +207,7 @@ async def health_services_endpoint( # noqa: PLR0915 "datadog_llm_observability", "generic_api", "arize", + "galileo", "sqs", ]: raise HTTPException( @@ -295,6 +297,19 @@ async def health_services_endpoint( # noqa: PLR0915 else "Arize is healthy" ), } + elif service == "galileo": + from litellm.integrations.galileo import GalileoObserve + + galileo_logger = GalileoObserve() + response = await galileo_logger.async_health_check() + return { + "status": response["status"], + "message": ( + response["error_message"] + if response["status"] == "unhealthy" + else "Galileo is healthy" + ), + } elif service == "langfuse": from litellm.integrations.langfuse.langfuse import LangFuseLogger diff --git a/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py b/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py index 80a4804956c..64c57ab90e3 100644 --- a/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py +++ b/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py @@ -696,6 +696,33 @@ async def test_test_model_connection_falls_back_to_deployments_zero_without_id() assert model_params.get("api_key") == "fake-key-A" +@pytest.mark.asyncio +@pytest.mark.parametrize( + "status,error_message", + [ + ("healthy", ""), + ("unhealthy", "Galileo authentication failed"), + ], +) +async def test_health_services_endpoint_galileo(status, error_message): + with patch("litellm.integrations.galileo.GalileoObserve") as MockGalileoObserve: + mock_instance = MagicMock() + mock_instance.async_health_check = AsyncMock( + return_value={"status": status, "error_message": error_message} + ) + MockGalileoObserve.return_value = mock_instance + + result = await health_services_endpoint(service="galileo") + + if status == "healthy": + assert result["status"] == "healthy" + assert result["message"] == "Galileo is healthy" + else: + assert result["status"] == "unhealthy" + assert result["message"] == error_message + mock_instance.async_health_check.assert_awaited_once() + + @pytest.mark.asyncio async def test_health_services_endpoint_datadog_llm_observability(): """