From a18fa85f766b121b9459ba2375c24fac4a1595ad Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Wed, 4 Nov 2026 19:21:08 +0100 Subject: [PATCH] feat: events when a model goes online or offline Webhooks and event functions had no way to know when a model appeared or disappeared from a connection, for example when a vLLM server stops answering. Open WebUI now sends a model online event when a model shows up in the model list and a model offline event when it drops out of it. The events are checked whenever the model list is fetched from the connections, so they are not a live health check. Nothing is sent for the first model list after startup. --- backend/open_webui/events.py | 6 ++++++ backend/open_webui/main.py | 1 + backend/open_webui/utils/models.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/backend/open_webui/events.py b/backend/open_webui/events.py index 24d4f18d97..2b628dd164 100644 --- a/backend/open_webui/events.py +++ b/backend/open_webui/events.py @@ -490,6 +490,12 @@ class EventDefinitions(BaseModel): MODEL_DISABLED: EventDefinition = EventDefinition( name='model.disabled', description='A model was disabled.', message='Model disabled' ) + MODEL_ONLINE: EventDefinition = EventDefinition( + name='model.online', description='A model became available.', message='Model online' + ) + MODEL_OFFLINE: EventDefinition = EventDefinition( + name='model.offline', description='A model is no longer available.', message='Model offline' + ) MODEL_ACCESS_UPDATED: EventDefinition = EventDefinition( name='model.access_updated', description='Model access was updated.', message='Model access updated' ) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 39ccdcded8..e9481cbac0 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -625,6 +625,7 @@ app.state.SCIM_TOKEN = SCIM_TOKEN # Models app.state.BASE_MODELS = [] +app.state.ONLINE_MODELS = None # WebUI diff --git a/backend/open_webui/utils/models.py b/backend/open_webui/utils/models.py index 40e44f303b..b618d12b14 100644 --- a/backend/open_webui/utils/models.py +++ b/backend/open_webui/utils/models.py @@ -9,6 +9,7 @@ from open_webui.config import ( DEFAULT_ARENA_MODEL, ) from open_webui.env import BYPASS_MODEL_ACCESS_CONTROL, ENABLE_FUNCTIONS, GLOBAL_LOG_LEVEL, REDIS_KEY_PREFIX +from open_webui.events import EVENTS, publish_event from open_webui.functions import get_function_models from open_webui.models.access_grants import AccessGrants from open_webui.models.config import Config @@ -90,6 +91,33 @@ async def get_all_base_models(request: Request, user: UserModel = None): return function_models + openai_models + ollama_models +async def publish_model_status_events(request: Request, base_models: list[dict]): + online_models = {model['id']: model.get('name') for model in base_models} + previous_models = request.app.state.ONLINE_MODELS + request.app.state.ONLINE_MODELS = online_models + if previous_models is None: + return + + for model_id in sorted(online_models.keys() - previous_models.keys()): + await publish_event( + request, + EVENTS.MODEL_ONLINE, + subject_id=model_id, + subject_type='model', + source='system', + data={'name': online_models[model_id]}, + ) + for model_id in sorted(previous_models.keys() - online_models.keys()): + await publish_event( + request, + EVENTS.MODEL_OFFLINE, + subject_id=model_id, + subject_type='model', + source='system', + data={'name': previous_models[model_id]}, + ) + + async def get_all_models(request, refresh: bool = False, user: UserModel = None): config = await Config.get_many( 'models.base_models_cache', @@ -123,6 +151,7 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None) if base_models is None: base_models = await get_all_base_models(request, user=user) + await publish_model_status_events(request, base_models) if base_models: request.app.state.BASE_MODELS = base_models if config.get('models.base_models_cache') and redis is not None: