diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 4891669ae3b..685d125b26c 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -188,13 +188,22 @@ class BaseLLMHTTPHandler: return None if isinstance(timeout, httpx.Timeout): return timeout - if isinstance(timeout, str): - if timeout.startswith("os.environ/"): - timeout = litellm.get_secret(timeout) # type: ignore[assignment] - if timeout is None: - return None - return float(timeout) - return float(timeout) + + timeout_value: Any = timeout + if isinstance(timeout, str) and timeout.startswith("os.environ/"): + timeout_value = litellm.get_secret(timeout) + if timeout_value is None: + return None + if isinstance(timeout_value, httpx.Timeout): + return timeout_value + + try: + return float(cast(Union[float, int, str], timeout_value)) + except (TypeError, ValueError): + verbose_logger.warning( + "Invalid Anthropic /v1/messages timeout value: %s", timeout_value + ) + return None @staticmethod def _get_anthropic_messages_timeout( diff --git a/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py b/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py index b7734eb6f72..0ebb9d7905e 100644 --- a/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py +++ b/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py @@ -116,6 +116,7 @@ def test_fingerprint_agentic_tools_is_deterministic(): "litellm_params,stream,expected_timeout", [ (GenericLiteLLMParams(timeout=1.25), False, 1.25), + (GenericLiteLLMParams(timeout=1.25), True, 1.25), (GenericLiteLLMParams(timeout=30, stream_timeout=0.75), True, 0.75), (GenericLiteLLMParams(request_timeout=2.5), False, 2.5), ], @@ -153,6 +154,25 @@ async def test_anthropic_messages_post_forwards_request_timeout( assert mock_client.post.call_args.kwargs["timeout"] == expected_timeout +def test_coerce_http_timeout_reads_env_secret(): + with patch("litellm.get_secret", return_value="3.5") as mock_get_secret: + result = BaseLLMHTTPHandler._coerce_http_timeout( + "os.environ/ANTHROPIC_MESSAGES_TIMEOUT" + ) + + assert result == 3.5 + mock_get_secret.assert_called_once_with("os.environ/ANTHROPIC_MESSAGES_TIMEOUT") + + +def test_coerce_http_timeout_returns_none_for_invalid_env_secret(): + with patch("litellm.get_secret", return_value="not-a-number"): + result = BaseLLMHTTPHandler._coerce_http_timeout( + "os.environ/ANTHROPIC_MESSAGES_TIMEOUT" + ) + + assert result is None + + @pytest.mark.asyncio async def test_async_anthropic_messages_handler_extra_headers(): """