From f8c0d2fdd690ecbe0444617c91dba3be45d5681e Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:33:24 +0200 Subject: [PATCH] fix: don't mutate caller's payload when resolving a pipe base model (#26906) generate_function_chat_completion rewrote form_data['model'] to the base model id in place. Because that dict is the same object process_chat_response later re-submits for the post-tool-call continuation, the continuation was access-checked against the base model instead of the user-facing preset. For a public preset over a private/unregistered pipe base, a non-admin's first message succeeded but the continuation after a native tool call was denied and silently swallowed, aborting the response (admins skip the user-only check, so they were unaffected). Operate on a shallow copy of form_data, mirroring the openai/ollama routers which already substitute the base model on a copy. The continuation now re-checks the preset the user actually has access to. Claude-Session: https://claude.ai/code/session_018toPfJW1hMXAhokGaL43Ep Co-authored-by: Claude --- backend/open_webui/functions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/open_webui/functions.py b/backend/open_webui/functions.py index 0cbfa8b980..6e82c0b022 100644 --- a/backend/open_webui/functions.py +++ b/backend/open_webui/functions.py @@ -206,6 +206,10 @@ async def generate_function_chat_completion(request, form_data, user, models: di return params + # Copy so the base-model substitution below doesn't leak into the caller's + # payload, which the tool-call continuation re-submits. Mirrors the routers. + form_data = {**form_data} + model_id = form_data.get('model') model_info = await Models.get_model_by_id(model_id)