From 4a7d4ebadac27d652ec200fa3939f10e9a5c17ed Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 27 Jul 2026 00:10:36 -0400 Subject: [PATCH] refac --- backend/open_webui/env.py | 10 ++++++++++ backend/open_webui/routers/ollama.py | 4 ++-- backend/open_webui/routers/openai.py | 10 +++++++--- backend/open_webui/utils/session_pool.py | 10 +++++++++- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index 7b58197c62..a466456fef 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -565,6 +565,16 @@ try: except (ValueError, TypeError): AIOHTTP_CLIENT_TIMEOUT = 300 +# Optional between-chunks idle cap for streaming aiohttp requests. +_stream_idle_timeout_raw = os.getenv('AIOHTTP_CLIENT_STREAM_IDLE_TIMEOUT', '') +try: + AIOHTTP_CLIENT_STREAM_IDLE_TIMEOUT = int(_stream_idle_timeout_raw) if _stream_idle_timeout_raw else None +except (ValueError, TypeError): + AIOHTTP_CLIENT_STREAM_IDLE_TIMEOUT = None + +if AIOHTTP_CLIENT_STREAM_IDLE_TIMEOUT is not None and AIOHTTP_CLIENT_STREAM_IDLE_TIMEOUT <= 0: + AIOHTTP_CLIENT_STREAM_IDLE_TIMEOUT = None + # SSL verification for general outbound requests (OpenAI, OAuth, etc.). # Accepts "True", "False", or a path to a CA bundle file. diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index 94d54c5299..4610146cc3 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -48,7 +48,7 @@ from open_webui.utils.payload import ( apply_model_params_to_body_openai, apply_system_prompt_to_body, ) -from open_webui.utils.session_pool import cleanup_response, get_session, stream_wrapper +from open_webui.utils.session_pool import cleanup_response, get_client_timeout, get_session, stream_wrapper log = logging.getLogger(__name__) @@ -130,7 +130,7 @@ async def send_request( data=payload, headers=headers, ssl=AIOHTTP_CLIENT_SESSION_SSL, - timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT), + timeout=get_client_timeout(stream=stream), ) if not r.ok: diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index 01d4a31a34..b5aac9e385 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -55,6 +55,7 @@ from open_webui.utils.payload import ( ) from open_webui.utils.session_pool import ( cleanup_response, + get_client_timeout, get_session, stream_wrapper, ) @@ -1344,6 +1345,7 @@ async def generate_chat_completion( part.get('text', '') for part in message['content'] if part.get('type') in ('input_text', 'text') ) + is_streaming_request = bool(payload.get('stream', False)) payload = json.dumps(payload) r = None @@ -1360,7 +1362,7 @@ async def generate_chat_completion( headers=headers, cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_SSL, - timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT), + timeout=get_client_timeout(stream=is_streaming_request), ) # Check if response is SSE @@ -1587,6 +1589,7 @@ async def responses( Routes to the correct upstream backend based on the model field. """ payload = form_data.model_dump(exclude_none=True) + is_streaming_request = bool(payload.get('stream', False)) idx = 0 model_id = form_data.model @@ -1637,7 +1640,7 @@ async def responses( headers=headers, cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_SSL, - timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT), + timeout=get_client_timeout(stream=is_streaming_request), ) # Check if response is SSE @@ -1707,6 +1710,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): payload = json.loads(body) except (json.JSONDecodeError, ValueError): payload = None + is_streaming_request = bool(payload.get('stream', False)) if isinstance(payload, dict) else False idx = 0 model_id = payload.get('model') if isinstance(payload, dict) else None @@ -1758,7 +1762,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): headers=headers, cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_SSL, - timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT), + timeout=get_client_timeout(stream=is_streaming_request), ) # Check if response is SSE diff --git a/backend/open_webui/utils/session_pool.py b/backend/open_webui/utils/session_pool.py index 90ca728bd9..414c45ed48 100644 --- a/backend/open_webui/utils/session_pool.py +++ b/backend/open_webui/utils/session_pool.py @@ -28,6 +28,7 @@ from typing import Optional import aiohttp from open_webui.env import ( + AIOHTTP_CLIENT_STREAM_IDLE_TIMEOUT, AIOHTTP_CLIENT_TIMEOUT, AIOHTTP_POOL_CONNECTIONS, AIOHTTP_POOL_CONNECTIONS_PER_HOST, @@ -39,6 +40,13 @@ log = logging.getLogger(__name__) _session: Optional[aiohttp.ClientSession] = None +def get_client_timeout(stream: bool = False) -> aiohttp.ClientTimeout: + return aiohttp.ClientTimeout( + total=AIOHTTP_CLIENT_TIMEOUT, + sock_read=AIOHTTP_CLIENT_STREAM_IDLE_TIMEOUT if stream else None, + ) + + async def get_session() -> aiohttp.ClientSession: """Return the shared aiohttp ClientSession, creating it lazily.""" global _session @@ -56,7 +64,7 @@ async def get_session() -> aiohttp.ClientSession: else: connector_kwargs['limit_per_host'] = 0 # aiohttp: 0 = unlimited connector = aiohttp.TCPConnector(**connector_kwargs) - timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT) + timeout = get_client_timeout() _session = aiohttp.ClientSession( connector=connector, timeout=timeout,