Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions litellm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,15 @@ def _dev_env_hot_reload_enabled() -> bool:
force_ipv4: bool = (
False # when True, litellm will force ipv4 for all LLM requests. Some users have seen httpx ConnectionError when using ipv6.
)
enable_http2: bool = (
False # opt-in: use HTTP/2 for outbound LLM requests. Forces the httpx transport (aiohttp cannot speak HTTP/2) and requires the `h2` package.
)
http2_max_connections: Optional[int] = (
None # when enable_http2 is True, max number of (multiplexed) connections in the httpx pool. None -> httpx default.
)
http2_max_keepalive_connections: Optional[int] = (
None # when enable_http2 is True, max number of idle keep-alive connections. None -> httpx default.
)
network_mock: bool = False # When True, use mock transport β€” no real network calls

####### STOP SEQUENCE LIMIT #######
Expand Down
298 changes: 268 additions & 30 deletions litellm/llms/custom_httpx/http_handler.py

Large diffs are not rendered by default.

30 changes: 29 additions & 1 deletion litellm/llms/openai/common_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
from litellm.llms.custom_httpx.http_handler import (
_DEFAULT_TTL_FOR_HTTPX_CLIENTS,
AsyncHTTPHandler,
HTTPHandler,
_get_http2_limits,
_should_enable_http2,
_verify_http2_available,
get_ssl_configuration,
)

Expand Down Expand Up @@ -224,17 +228,36 @@ def _get_async_http_client(
# Get unified SSL configuration
ssl_config = get_ssl_configuration()

return httpx.AsyncClient(
# Respect the opt-in outbound HTTP/2 setting. A shared aiohttp session
# cannot speak HTTP/2, so it takes priority (AsyncHTTPHandler emits the
# warning in that path); here we just resolve http2 off when one is given.
http2_enabled = _should_enable_http2() and shared_session is None
http2_limits = _get_http2_limits() if http2_enabled else None
if http2_enabled:
_verify_http2_available()

client_kwargs: dict = dict(
verify=ssl_config,
transport=AsyncHTTPHandler._create_async_transport(
ssl_context=(
ssl_config if isinstance(ssl_config, ssl.SSLContext) else None
),
ssl_verify=ssl_config if isinstance(ssl_config, bool) else None,
shared_session=shared_session,
http2=http2_enabled,
limits=http2_limits,
),
follow_redirects=True,
)
if http2_enabled:
# Honored only when httpx builds its own transport (transport=None,
# i.e. no force_ipv4); ignored on the explicit-transport path which
# already carries http2/limits.
client_kwargs["http2"] = True
if http2_limits is not None:
client_kwargs["limits"] = http2_limits

return httpx.AsyncClient(**client_kwargs)

@staticmethod
def _get_sync_http_client() -> Optional[httpx.Client]:
Expand All @@ -249,6 +272,11 @@ def _get_sync_http_client() -> Optional[httpx.Client]:
# Get unified SSL configuration
ssl_config = get_ssl_configuration()

# Respect the opt-in outbound HTTP/2 setting. HTTPHandler centralizes the
# transport/limits wiring (incl. force_ipv4), so reuse its client.
if _should_enable_http2():
return HTTPHandler(ssl_verify=ssl_config).client
Comment on lines +277 to +278

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Throwaway HTTPHandler created to extract .client

HTTPHandler(ssl_verify=ssl_config).client instantiates a full HTTPHandler object (including a concurrent_requests semaphore, default-header computation, and the _verify_http2_available() side-effect on _HTTP2_AVAILABLE) just to return .client. The HTTPHandler wrapper is then discarded. By contrast, the async path in this same file builds the httpx.AsyncClient inline. For consistency and to avoid the wasted allocation, the sync path could also build the httpx.Client inline (mirroring the async path), or delegate to _get_httpx_client() and return .client from the cached instance.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


return httpx.Client(
verify=ssl_config,
follow_redirects=True,
Expand Down
7 changes: 6 additions & 1 deletion litellm/proxy/health_endpoints/_health_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
from litellm._logging import verbose_logger, verbose_proxy_logger
from litellm.constants import HEALTH_CHECK_TIMEOUT_SECONDS
from litellm.litellm_core_utils.custom_logger_registry import CustomLoggerRegistry
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
from litellm.llms.custom_httpx.http_handler import (
AsyncHTTPHandler,
_should_enable_http2,
)
from litellm.proxy._types import (
AlertType,
CallInfo,
Expand Down Expand Up @@ -1529,6 +1532,7 @@ async def _get_health_readiness_details(
"litellm_version": version,
"success_callbacks": success_callback_names,
"use_aiohttp_transport": AsyncHTTPHandler._should_use_aiohttp_transport(),
"enable_http2": _should_enable_http2(),
"log_level": log_level_name,
"is_detailed_debug": is_detailed_debug,
}
Expand All @@ -1540,6 +1544,7 @@ async def _get_health_readiness_details(
"litellm_version": version,
"success_callbacks": success_callback_names,
"use_aiohttp_transport": AsyncHTTPHandler._should_use_aiohttp_transport(),
"enable_http2": _should_enable_http2(),
"log_level": log_level_name,
"is_detailed_debug": is_detailed_debug,
}
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ utils = [
"numpydoc>=1.8.0,<2.0",
]
caching = ["diskcache>=5.6.3,<6.0"]
# HTTP/2 for outbound requests (opt-in via litellm.enable_http2). `h2` is httpx's
# HTTP/2 backend; kept out of core so the base SDK stays light.
http2 = ["h2>=4.0.0,<5.0"]
semantic-router = [
"semantic-router>=0.1.15,<1.0; python_version < '3.14'",
"aurelio-sdk>=0.0.19,<1.0; python_version < '3.14'",
Expand Down
Loading
Loading