llm-api-proxy / src /rotator_library /timeout_config.py
Mirrowel
fix(timeout): πŸ› increase streaming read timeout to 5 minutes - 3 minute timeouts failed sometimes
0a45558
# src/rotator_library/timeout_config.py
"""
Centralized timeout configuration for HTTP requests.
All values can be overridden via environment variables:
TIMEOUT_CONNECT - Connection establishment timeout (default: 30s)
TIMEOUT_WRITE - Request body send timeout (default: 30s)
TIMEOUT_POOL - Connection pool acquisition timeout (default: 60s)
TIMEOUT_READ_STREAMING - Read timeout between chunks for streaming (default: 180s / 3 min)
TIMEOUT_READ_NON_STREAMING - Read timeout for non-streaming responses (default: 600s / 10 min)
"""
import os
import logging
import httpx
lib_logger = logging.getLogger("rotator_library")
class TimeoutConfig:
"""
Centralized timeout configuration for HTTP requests.
All values can be overridden via environment variables.
"""
# Default values (in seconds)
_CONNECT = 30.0
_WRITE = 30.0
_POOL = 60.0
_READ_STREAMING = 300.0 # 5 minutes between chunks
_READ_NON_STREAMING = 600.0 # 10 minutes for full response
@classmethod
def _get_env_float(cls, key: str, default: float) -> float:
"""Get a float value from environment variable, or return default."""
value = os.environ.get(key)
if value is not None:
try:
return float(value)
except ValueError:
lib_logger.warning(
f"Invalid value for {key}: {value}. Using default: {default}"
)
return default
@classmethod
def connect(cls) -> float:
"""Connection establishment timeout."""
return cls._get_env_float("TIMEOUT_CONNECT", cls._CONNECT)
@classmethod
def write(cls) -> float:
"""Request body send timeout."""
return cls._get_env_float("TIMEOUT_WRITE", cls._WRITE)
@classmethod
def pool(cls) -> float:
"""Connection pool acquisition timeout."""
return cls._get_env_float("TIMEOUT_POOL", cls._POOL)
@classmethod
def read_streaming(cls) -> float:
"""Read timeout between chunks for streaming requests."""
return cls._get_env_float("TIMEOUT_READ_STREAMING", cls._READ_STREAMING)
@classmethod
def read_non_streaming(cls) -> float:
"""Read timeout for non-streaming responses."""
return cls._get_env_float("TIMEOUT_READ_NON_STREAMING", cls._READ_NON_STREAMING)
@classmethod
def streaming(cls) -> httpx.Timeout:
"""
Timeout configuration for streaming LLM requests.
Uses a shorter read timeout (default 3 min) since we expect
periodic chunks. If no data arrives for this duration, the
connection is considered stalled.
"""
return httpx.Timeout(
connect=cls.connect(),
read=cls.read_streaming(),
write=cls.write(),
pool=cls.pool(),
)
@classmethod
def non_streaming(cls) -> httpx.Timeout:
"""
Timeout configuration for non-streaming LLM requests.
Uses a longer read timeout (default 10 min) since the server
may take significant time to generate the complete response
before sending anything back.
"""
return httpx.Timeout(
connect=cls.connect(),
read=cls.read_non_streaming(),
write=cls.write(),
pool=cls.pool(),
)