diff --git a/docs/my-website/docs/guides/security_settings.md b/docs/my-website/docs/guides/security_settings.md
index 4dfeda2d70..008e620c51 100644
--- a/docs/my-website/docs/guides/security_settings.md
+++ b/docs/my-website/docs/guides/security_settings.md
@@ -1,14 +1,14 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-# SSL Security Settings
+# SSL, HTTP Proxy Security Settings
If you're in an environment using an older TTS bundle, with an older encryption, follow this guide.
LiteLLM uses HTTPX for network requests, unless otherwise specified.
-1. Disable SSL verification
+## 1. Disable SSL verification
@@ -35,7 +35,7 @@ export SSL_VERIFY="False"
-2. Lower security settings
+## 2. Lower security settings
@@ -63,4 +63,29 @@ export SSL_CERTIFICATE="/path/to/certificate.pem"
+## 3. Use HTTP_PROXY environment variable
+
+Both httpx and aiohttp libraries use `urllib.request.getproxies` from environment variables. Before client initialization, you may set proxy (and optional SSL_CERT_FILE) by setting the environment variables:
+
+
+
+
+```python
+import litellm
+litellm.aiohttp_trust_env = True
+```
+
+```bash
+export HTTPS_PROXY='http://username:password@proxy_uri:port'
+```
+
+
+
+
+```bash
+export HTTPS_PROXY='http://username:password@proxy_uri:port'
+export AIOHTTP_TRUST_ENV='True'
+```
+
+
diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md
index ce9a938078..37efe0fc33 100644
--- a/docs/my-website/docs/proxy/config_settings.md
+++ b/docs/my-website/docs/proxy/config_settings.md
@@ -307,6 +307,7 @@ router_settings:
| AGENTOPS_SERVICE_NAME | Service Name for AgentOps logging integration
| AISPEND_ACCOUNT_ID | Account ID for AI Spend
| AISPEND_API_KEY | API Key for AI Spend
+| AIOHTTP_TRUST_ENV | Flag to enable aiohttp trust environment. When this is set to True, aiohttp will respect HTTP(S)_PROXY env vars. **Default is False**
| ALLOWED_EMAIL_DOMAINS | List of email domains allowed for access
| ARIZE_API_KEY | API key for Arize platform integration
| ARIZE_SPACE_KEY | Space key for Arize platform
diff --git a/litellm/__init__.py b/litellm/__init__.py
index c2064d5d57..1ba7d663d8 100644
--- a/litellm/__init__.py
+++ b/litellm/__init__.py
@@ -323,6 +323,7 @@ priority_reservation: Optional[Dict[str, float]] = None
use_aiohttp_transport: bool = (
True # Older variable, aiohttp is now the default. use disable_aiohttp_transport instead.
)
+aiohttp_trust_env: bool = False # set to true to use HTTP_ Proxy settings
disable_aiohttp_transport: bool = False # Set this to true to use httpx instead
disable_aiohttp_trust_env: bool = False # When False, aiohttp will respect HTTP(S)_PROXY env vars
force_ipv4: bool = (
diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py
index 1d68702d2e..34968a63ae 100644
--- a/litellm/llms/custom_httpx/http_handler.py
+++ b/litellm/llms/custom_httpx/http_handler.py
@@ -580,15 +580,24 @@ class AsyncHTTPHandler:
- True: use default SSL verification (equivalent to ssl.create_default_context())
"""
from litellm.llms.custom_httpx.aiohttp_transport import LiteLLMAiohttpTransport
+ from litellm.secret_managers.main import str_to_bool
connector_kwargs = AsyncHTTPHandler._get_ssl_connector_kwargs(
ssl_verify=ssl_verify, ssl_context=ssl_context
)
+ #########################################################
+ # Check if user enabled aiohttp trust env
+ # use for HTTP_PROXY, HTTPS_PROXY, etc.
+ ########################################################
+ trust_env: bool = litellm.aiohttp_trust_env
+ if str_to_bool(os.getenv("AIOHTTP_TRUST_ENV", "False")) is True:
+ trust_env = True
verbose_logger.debug("Creating AiohttpTransport...")
return LiteLLMAiohttpTransport(
client=lambda: ClientSession(
- connector=TCPConnector(**connector_kwargs)
+ connector=TCPConnector(**connector_kwargs),
+ trust_env=trust_env,
),
)
diff --git a/tests/test_litellm/llms/custom_httpx/test_http_handler.py b/tests/test_litellm/llms/custom_httpx/test_http_handler.py
index bc960fdc22..6f66bf20a0 100644
--- a/tests/test_litellm/llms/custom_httpx/test_http_handler.py
+++ b/tests/test_litellm/llms/custom_httpx/test_http_handler.py
@@ -123,6 +123,34 @@ async def test_ssl_verification_with_aiohttp_transport():
assert transport_connector._ssl == aiohttp_session.connector._ssl
+@pytest.mark.asyncio
+async def test_aiohttp_transport_trust_env_setting(monkeypatch):
+ """Test that trust_env setting is properly configured in aiohttp transport"""
+ # Test 1: Default trust_env behavior
+ transport = AsyncHTTPHandler._create_aiohttp_transport()
+ client_session = transport._get_valid_client_session()
+
+ # Default should be False (litellm.aiohttp_trust_env default)
+ default_trust_env = getattr(litellm, 'aiohttp_trust_env', False)
+ assert client_session._trust_env == default_trust_env
+
+ # Test 2: Environment variable override
+ monkeypatch.setenv("AIOHTTP_TRUST_ENV", "True")
+ transport_with_env = AsyncHTTPHandler._create_aiohttp_transport()
+ client_session_with_env = transport_with_env._get_valid_client_session()
+
+ # Should be True when environment variable is set
+ assert client_session_with_env._trust_env is True
+
+ # Test 3: Verify environment variable with False value
+ monkeypatch.setenv("AIOHTTP_TRUST_ENV", "False")
+ transport_with_false_env = AsyncHTTPHandler._create_aiohttp_transport()
+ client_session_with_false_env = transport_with_false_env._get_valid_client_session()
+
+ # Should respect the litellm.aiohttp_trust_env setting when env var is False
+ assert client_session_with_false_env._trust_env == default_trust_env
+
+
def test_get_ssl_context():
"""Test that _get_ssl_context() returns a proper SSL context with certifi CA bundle"""
with patch('ssl.create_default_context') as mock_create_context:
diff --git a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py
index 40f914d726..710e426501 100644
--- a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py
+++ b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py
@@ -103,10 +103,12 @@ def test_mock_create_audio_file(mocker: MockerFixture, monkeypatch, llm_router:
"""
Asserts 'create_file' is called with the correct arguments
"""
+ import litellm
from litellm import Router
from litellm.proxy.utils import ProxyLogging
- mock_create_file = mocker.patch("litellm.files.main.create_file")
+ # Mock create_file as an async function
+ mock_create_file = mocker.patch("litellm.files.main.create_file", new=mocker.AsyncMock())
proxy_logging_obj = ProxyLogging(
user_api_key_cache=DualCache(default_in_memory_ttl=1)
@@ -114,6 +116,61 @@ def test_mock_create_audio_file(mocker: MockerFixture, monkeypatch, llm_router:
proxy_logging_obj._add_proxy_hooks(llm_router)
+ # Add managed_files hook to ensure the test reaches the mocked function
+ from litellm.llms.base_llm.files.transformation import BaseFileEndpoints
+
+ class DummyManagedFiles(BaseFileEndpoints):
+ async def acreate_file(self, llm_router, create_file_request, target_model_names_list, litellm_parent_otel_span, user_api_key_dict):
+ # Handle both dict and object forms of create_file_request
+ if isinstance(create_file_request, dict):
+ file_data = create_file_request.get("file")
+ purpose_data = create_file_request.get("purpose")
+ else:
+ file_data = create_file_request.file
+ purpose_data = create_file_request.purpose
+
+ # Call the mocked litellm.files.main.create_file to ensure asserts work
+ await litellm.files.main.create_file(
+ custom_llm_provider="azure",
+ model="azure/chatgpt-v-2",
+ api_key="azure_api_key",
+ file=file_data,
+ purpose=purpose_data,
+ )
+ await litellm.files.main.create_file(
+ custom_llm_provider="openai",
+ model="openai/gpt-3.5-turbo",
+ api_key="openai_api_key",
+ file=file_data,
+ purpose=purpose_data,
+ )
+ # Return a dummy response object as needed by the test
+ from litellm.types.llms.openai import OpenAIFileObject
+ return OpenAIFileObject(
+ id="dummy-id",
+ object="file",
+ bytes=len(file_data[1]) if file_data else 0,
+ created_at=1234567890,
+ filename=file_data[0] if file_data else "test.wav",
+ purpose=purpose_data,
+ status="uploaded",
+ )
+
+ async def afile_retrieve(self, file_id, litellm_parent_otel_span):
+ raise NotImplementedError("Not implemented for test")
+
+ async def afile_list(self, purpose, litellm_parent_otel_span):
+ raise NotImplementedError("Not implemented for test")
+
+ async def afile_delete(self, file_id, litellm_parent_otel_span):
+ raise NotImplementedError("Not implemented for test")
+
+ async def afile_content(self, file_id, litellm_parent_otel_span):
+ raise NotImplementedError("Not implemented for test")
+
+ # Manually add the hook to the proxy_hook_mapping
+ proxy_logging_obj.proxy_hook_mapping["managed_files"] = DummyManagedFiles()
+
monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", llm_router)
monkeypatch.setattr(
"litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_obj
@@ -133,8 +190,7 @@ def test_mock_create_audio_file(mocker: MockerFixture, monkeypatch, llm_router:
headers={"Authorization": "Bearer test-key"},
)
- print(f"response: {response.text}")
- # assert response.status_code == 200
+ assert response.status_code == 200
# Get all calls made to create_file
calls = mock_create_file.call_args_list