diff --git a/enterprise/dist/litellm_enterprise-0.1.32-py3-none-any.whl b/enterprise/dist/litellm_enterprise-0.1.32-py3-none-any.whl new file mode 100644 index 0000000000..0c87c72c98 Binary files /dev/null and b/enterprise/dist/litellm_enterprise-0.1.32-py3-none-any.whl differ diff --git a/enterprise/dist/litellm_enterprise-0.1.32.tar.gz b/enterprise/dist/litellm_enterprise-0.1.32.tar.gz new file mode 100644 index 0000000000..4f0ac1a9b2 Binary files /dev/null and b/enterprise/dist/litellm_enterprise-0.1.32.tar.gz differ diff --git a/enterprise/pyproject.toml b/enterprise/pyproject.toml index eca5cdb97d..55720934f0 100644 --- a/enterprise/pyproject.toml +++ b/enterprise/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm-enterprise" -version = "0.1.31" +version = "0.1.32" description = "Package for LiteLLM Enterprise features" authors = ["BerriAI"] readme = "README.md" @@ -22,7 +22,7 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "0.1.31" +version = "0.1.32" version_files = [ "pyproject.toml:version", "../requirements.txt:litellm-enterprise==", diff --git a/poetry.lock b/poetry.lock index dfd261d01f..a4ee1b39f4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3163,15 +3163,15 @@ openai = ["openai (>=0.27.8)"] [[package]] name = "litellm-enterprise" -version = "0.1.31" +version = "0.1.32" description = "Package for LiteLLM Enterprise features" optional = true python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" groups = ["main"] markers = "extra == \"proxy\"" files = [ - {file = "litellm_enterprise-0.1.31-py3-none-any.whl", hash = "sha256:7b0f750343e6f28c88e1557c656a6bea50fa6c8990a13e86ea20497cf666c79b"}, - {file = "litellm_enterprise-0.1.31.tar.gz", hash = "sha256:684d09daa3ededf1394df4ec1439aab606b884b68af2c92c478af0784a30e588"}, + {file = "litellm_enterprise-0.1.32-py3-none-any.whl", hash = "sha256:db043d2628e6a6a1369b3d582360fc5c6676bb213b53a4e6dd35d0c563d4d3e5"}, + {file = "litellm_enterprise-0.1.32.tar.gz", hash = "sha256:5648f982f92a3a2323ed49c3eee61e281e4ccb284dd8c45ee997dadea0f56a5a"}, ] [[package]] @@ -7701,4 +7701,4 @@ utils = ["numpydoc"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "cb200f60a9866eaca7e427d42ea7df6f5df4e10b9dba3f998e9c84789762fa67" +content-hash = "a4aeafda4e80164ff378a73d218584feca7eaa39127f87ed6e27a818d3fd6159" diff --git a/pyproject.toml b/pyproject.toml index 4f388d0fad..f943cc22cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ mcp = {version = ">=1.25.0,<2.0.0", optional = true, python = ">=3.10"} a2a-sdk = {version = "^0.3.22", optional = true, python = ">=3.10"} litellm-proxy-extras = {version = "0.4.38", optional = true} rich = {version = "13.7.1", optional = true} -litellm-enterprise = {version = "0.1.31", optional = true} +litellm-enterprise = {version = "0.1.32", optional = true} diskcache = {version = "^5.6.1", optional = true} polars = {version = "^1.31.0", optional = true, python = ">=3.10"} semantic-router = {version = ">=0.1.12", optional = true, python = ">=3.9,<3.14"} diff --git a/requirements.txt b/requirements.txt index cbfa081bf5..ce008fdb1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -78,4 +78,4 @@ pypdf>=6.6.2 # for PDF text extraction in RAG ingestion ######################## # LITELLM ENTERPRISE DEPENDENCIES ######################## -litellm-enterprise==0.1.31 +litellm-enterprise==0.1.32 diff --git a/tests/test_litellm/test_ssl_verify_unit.py b/tests/test_litellm/test_ssl_verify_unit.py index 2bc63d01b2..a2e04fce74 100644 --- a/tests/test_litellm/test_ssl_verify_unit.py +++ b/tests/test_litellm/test_ssl_verify_unit.py @@ -5,14 +5,16 @@ These tests verify that ssl_verify parameters are correctly propagated through the call stack without requiring live API credentials. """ -import pytest -from unittest.mock import Mock, patch -from pathlib import Path import sys +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest # Add litellm to path sys.path.insert(0, str(Path(__file__).parent)) +import litellm.proxy.guardrails.guardrail_hooks.aim.aim as _aim_module from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM from litellm.llms.bedrock.chat.invoke_handler import BedrockLLM from litellm.proxy.guardrails.guardrail_hooks.aim.aim import AimGuardrail @@ -103,36 +105,37 @@ class TestBedrockLLMSSLVerify: class TestAimGuardrailSSLVerify: """Test SSL verification parameter handling in AimGuardrail.""" - @patch("litellm.proxy.guardrails.guardrail_hooks.aim.aim.get_async_httpx_client") - def test_init_accepts_ssl_verify(self, mock_get_client): + def test_init_accepts_ssl_verify(self): """Test that AimGuardrail.__init__ accepts and uses ssl_verify parameter.""" mock_handler = Mock() - mock_get_client.return_value = mock_handler - # Initialize with ssl_verify - cert_path = "/path/to/aim_cert.pem" - AimGuardrail( - api_key="test_key", api_base="https://test.aim.api", ssl_verify=cert_path - ) + # Use patch.object on the actual module reference for reliable patching + # across different import orders / CI environments + with patch.object(_aim_module, "get_async_httpx_client", return_value=mock_handler) as mock_get_client: + # Initialize with ssl_verify + cert_path = "/path/to/aim_cert.pem" + AimGuardrail( + api_key="test_key", api_base="https://test.aim.api", ssl_verify=cert_path + ) - # Verify get_async_httpx_client was called with ssl_verify in params - assert mock_get_client.called - call_kwargs = mock_get_client.call_args[1] - assert "params" in call_kwargs - assert call_kwargs["params"] is not None - assert call_kwargs["params"]["ssl_verify"] == cert_path + # Verify get_async_httpx_client was called with ssl_verify in params + assert mock_get_client.called + call_kwargs = mock_get_client.call_args[1] + assert "params" in call_kwargs + assert call_kwargs["params"] is not None + assert call_kwargs["params"]["ssl_verify"] == cert_path - @patch("litellm.proxy.guardrails.guardrail_hooks.aim.aim.get_async_httpx_client") - def test_init_without_ssl_verify(self, mock_get_client): + def test_init_without_ssl_verify(self): """Test that AimGuardrail works without ssl_verify parameter.""" mock_handler = Mock() - mock_get_client.return_value = mock_handler - # Initialize without ssl_verify - AimGuardrail(api_key="test_key", api_base="https://test.aim.api") + # Use patch.object on the actual module reference for reliable patching + with patch.object(_aim_module, "get_async_httpx_client", return_value=mock_handler) as mock_get_client: + # Initialize without ssl_verify + AimGuardrail(api_key="test_key", api_base="https://test.aim.api") - # Should still work, just without custom SSL - assert mock_get_client.called + # Should still work, just without custom SSL + assert mock_get_client.called class TestHTTPHandlerSSLVerify: diff --git a/tests/test_litellm/test_video_generation.py b/tests/test_litellm/test_video_generation.py index c8cc292519..2eb16c65ae 100644 --- a/tests/test_litellm/test_video_generation.py +++ b/tests/test_litellm/test_video_generation.py @@ -797,35 +797,30 @@ def test_openai_transform_video_content_request_empty_params(): def test_video_content_handler_uses_get_for_openai(): """HTTP handler must use GET (not POST) for OpenAI content download.""" + from litellm.llms.custom_httpx.http_handler import HTTPHandler from litellm.types.router import GenericLiteLLMParams handler = BaseLLMHTTPHandler() config = OpenAIVideoConfig() - mock_client = MagicMock() + # Use spec=HTTPHandler so isinstance(mock_client, HTTPHandler) returns True, + # ensuring the handler uses our mock directly instead of creating a new client. + mock_client = MagicMock(spec=HTTPHandler) mock_response = MagicMock() mock_response.content = b"mp4-bytes" mock_client.get.return_value = mock_response - # Patch both where _get_httpx_client is used and where it is defined so the mock - # is used regardless of import order / CI environment - with patch( - "litellm.llms.custom_httpx.http_handler._get_httpx_client", - return_value=mock_client, - ), patch( - "litellm.llms.custom_httpx.llm_http_handler._get_httpx_client", - return_value=mock_client, - ): - result = handler.video_content_handler( - video_id="video_abc", - video_content_provider_config=config, - custom_llm_provider="openai", - litellm_params=GenericLiteLLMParams(api_base="https://api.openai.com/v1"), - logging_obj=MagicMock(), - timeout=5.0, - api_key="sk-test", - _is_async=False, - ) + result = handler.video_content_handler( + video_id="video_abc", + video_content_provider_config=config, + custom_llm_provider="openai", + litellm_params=GenericLiteLLMParams(api_base="https://api.openai.com/v1"), + logging_obj=MagicMock(), + timeout=5.0, + api_key="sk-test", + client=mock_client, + _is_async=False, + ) assert result == b"mp4-bytes" mock_client.get.assert_called_once()