From 95667c2e47774194e3fbc34cb21f425d19988092 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Sat, 5 Sep 2026 12:50:30 +0200 Subject: [PATCH] fix: ignore Open WebUI-owned keys in upstream model listings Models from an OpenAI-compatible connection are merged with the upstream response spread in verbatim, so a gateway can set keys Open WebUI assigns itself. A gateway sending `preset` makes the admin model list treat a model that has no workspace entry as a preset, so clicking Edit opens the workspace model editor on an id that does not exist there and the request 404s. `pipe`, `arena`, `action_ids` and `filter_ids` do more damage: they route chat completions to a local function that is not there, let access be decided by upstream-supplied grants, and attach local functions to a remote model. Upstream entries are now merged without those keys. Every other field, including ones Open WebUI knows nothing about, still passes through untouched, and models that Open WebUI itself marks as presets are unaffected. One consequence worth calling out: when a connection points at another Open WebUI instance, that instance's `info` block is no longer inherited, so those models lose the remote description, icon and capability flags. Inheriting another instance's database-shaped state, down to its `user_id` and access grants, is what makes this whole class of bug possible, so it goes with the rest. Fixes #29629 --- backend/open_webui/routers/openai.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index d7856bea48..ff8b3c4d04 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -77,6 +77,7 @@ log = logging.getLogger(__name__) _STRIP_PROXY_HEADERS = frozenset({'Content-Encoding', 'Content-Length', 'Transfer-Encoding'}) _MODEL_LIST_TIMEOUT = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST) _UNSUPPORTED_OPENAI_MODEL_KEYWORDS = ('babbage', 'dall-e', 'davinci', 'embedding', 'tts', 'whisper') +_STRIP_MODEL_KEYS = frozenset({'action_ids', 'arena', 'filter_ids', 'info', 'pipe', 'preset'}) BASE_MODELS_CACHE_KEY = f'{REDIS_KEY_PREFIX}:models:base' @@ -85,6 +86,11 @@ def _clean_proxy_headers(raw_headers) -> dict: return {k: v for k, v in raw_headers.items() if k not in _STRIP_PROXY_HEADERS} +def _clean_model_keys(model: dict) -> dict: + """Return a copy of *model* without the keys Open WebUI assigns itself.""" + return {k: v for k, v in model.items() if k not in _STRIP_MODEL_KEYS} + + async def send_get_request( request: Request = None, url=None, @@ -835,7 +841,7 @@ async def get_all_models(request: Request, user: UserModel) -> dict[str, list]: if model_id and model_id not in models: provider = model.get('provider', '') merged = { - **model, + **_clean_model_keys(model), 'name': model.get('name', model_id), 'owned_by': 'openai', 'openai': model,