fix(custom_httpx): report handler timeout in sync errors

This commit is contained in:
Sisyphus 2026-08-30 16:20:35 +08:00
parent 2f02e33db5
commit 5998fd124f
2 changed files with 22 additions and 6 deletions

View file

@ -163,15 +163,21 @@ def _default_cached_client_timeout() -> httpx.Timeout:
return httpx.Timeout(timeout=configured, connect=HTTP_HANDLER_CONNECT_TIMEOUT_SECONDS)
def _resolve_effective_timeout(timeout: float | httpx.Timeout | None) -> float | httpx.Timeout:
def _resolve_effective_timeout(
timeout: float | httpx.Timeout | None,
fallback_timeout: float | httpx.Timeout | None = None,
) -> float | httpx.Timeout:
"""Resolve the timeout actually in effect for a request.
`None` means "use the client default", which `create_client` maps to
`_DEFAULT_TIMEOUT`. An explicit `timeout=None` handed to httpx bypasses the
client default entirely (no timeouts are enforced), so `None` must be
resolved before building a request or rendering a Timeout error message.
`fallback_timeout` lets error messages preserve a handler-level timeout.
"""
return timeout if timeout is not None else _DEFAULT_TIMEOUT
if timeout is not None:
return timeout
return fallback_timeout if fallback_timeout is not None else _DEFAULT_TIMEOUT
_CLIENT_REFCOUNT_WHEN_HANDLER_IS_SOLE_REFERRER: Final = 2
@ -1377,7 +1383,7 @@ class HTTPHandler:
return response
except httpx.TimeoutException:
raise litellm.Timeout(
message=f"Connection timed out after {_resolve_effective_timeout(timeout)} seconds.",
message=f"Connection timed out after {_resolve_effective_timeout(timeout, self.timeout)} seconds.",
model="default-model-name",
llm_provider="litellm-httpx-handler",
)
@ -1427,7 +1433,7 @@ class HTTPHandler:
return response
except httpx.TimeoutException:
raise litellm.Timeout(
message=f"Connection timed out after {_resolve_effective_timeout(timeout)} seconds.",
message=f"Connection timed out after {_resolve_effective_timeout(timeout, self.timeout)} seconds.",
model="default-model-name",
llm_provider="litellm-httpx-handler",
)
@ -1476,7 +1482,7 @@ class HTTPHandler:
return response
except httpx.TimeoutException:
raise litellm.Timeout(
message=f"Connection timed out after {_resolve_effective_timeout(timeout)} seconds.",
message=f"Connection timed out after {_resolve_effective_timeout(timeout, self.timeout)} seconds.",
model="default-model-name",
llm_provider="litellm-httpx-handler",
)
@ -1526,7 +1532,7 @@ class HTTPHandler:
return response
except httpx.TimeoutException:
raise litellm.Timeout(
message=f"Connection timed out after {_resolve_effective_timeout(timeout)} seconds.",
message=f"Connection timed out after {_resolve_effective_timeout(timeout, self.timeout)} seconds.",
model="default-model-name",
llm_provider="litellm-httpx-handler",
)

View file

@ -1603,6 +1603,16 @@ def test_sync_post_timeout_message_reports_client_default_when_timeout_unset():
assert f"{COMPLETION_HTTP_FALLBACK_SECONDS}" in str(exc_info.value)
def test_sync_post_timeout_message_reports_handler_timeout_when_request_timeout_unset():
handler = HTTPHandler(timeout=7.25)
handler.client = _RecordingSyncClient()
with pytest.raises(litellm.Timeout) as exc_info:
handler.post("https://example.test/v1/chat", json={"ping": True})
assert "7.25 seconds" in str(exc_info.value)
def test_sync_post_timeout_message_reports_explicit_timeout():
handler = HTTPHandler()
handler.client = _RecordingSyncClient()