diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 262e7c0818..75b4b6d284 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -2097,6 +2097,7 @@ ENABLE_USER_WEBHOOKS = os.getenv('ENABLE_USER_WEBHOOKS', 'False').lower() == 'tr # FastAPI / AnyIO settings THREAD_POOL_SIZE = os.getenv('THREAD_POOL_SIZE', None) +THREAD_POOL_THREAD_NAME_PREFIX = os.getenv('THREAD_POOL_THREAD_NAME_PREFIX', '') if THREAD_POOL_SIZE is not None and isinstance(THREAD_POOL_SIZE, str): try: diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 9c5f538562..6cbf0155cb 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -7,6 +7,7 @@ import mimetypes import os import sys import time +from concurrent.futures import ThreadPoolExecutor from contextlib import asynccontextmanager from uuid import uuid4 @@ -64,6 +65,7 @@ from open_webui.config import ( ONEDRIVE_SHAREPOINT_URL, STATIC_DIR, THREAD_POOL_SIZE, + THREAD_POOL_THREAD_NAME_PREFIX, WEBUI_AUTH, WEBUI_NAME, async_reset_config, @@ -343,6 +345,16 @@ async def lifespan(app: FastAPI): # This allows sync functions to schedule work on the main loop without blocking health checks app.state.main_loop = asyncio.get_running_loop() + if THREAD_POOL_SIZE and THREAD_POOL_SIZE > 0: + # asyncio offloads bypass AnyIO's limiter, so configure both before the first offload. + anyio.to_thread.current_default_thread_limiter().total_tokens = THREAD_POOL_SIZE + app.state.main_loop.set_default_executor( + ThreadPoolExecutor( + max_workers=THREAD_POOL_SIZE, + thread_name_prefix=THREAD_POOL_THREAD_NAME_PREFIX, + ) + ) + app.state.instance_id = INSTANCE_ID start_logger() @@ -378,10 +390,6 @@ async def lifespan(app: FastAPI): if app.state.redis is not None: app.state.redis_task_command_listener = asyncio.create_task(redis_task_command_listener(app)) - if THREAD_POOL_SIZE and THREAD_POOL_SIZE > 0: - limiter = anyio.to_thread.current_default_thread_limiter() - limiter.total_tokens = THREAD_POOL_SIZE - app.state.periodic_usage_pool_cleanup = asyncio.create_task(periodic_usage_pool_cleanup()) app.state.periodic_session_pool_cleanup = asyncio.create_task(periodic_session_pool_cleanup())