Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion backend/open_webui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@
)
from open_webui.utils.embeddings import generate_embeddings
from open_webui.utils.middleware import process_chat_payload, process_chat_response
from open_webui.utils.access_control import has_access
from open_webui.utils.access_control import has_access, has_permission
from open_webui.utils.system_prompts import remove_system_prompts_from_body

from open_webui.utils.auth import (
get_license_data,
Expand Down Expand Up @@ -1297,6 +1298,12 @@ async def chat_completion(
form_data: dict,
user=Depends(get_verified_user),
):
if user.role != "admin" and not has_permission(
user.id, "chat.system_prompt", request.app.state.config.USER_PERMISSIONS
):
form_data = remove_system_prompts_from_body(form_data)
request.state.system_prompt_permission_checked = True

if not request.app.state.MODELS:
await get_all_models(request, user=user)

Expand Down
24 changes: 23 additions & 1 deletion backend/open_webui/routers/ollama.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@
apply_model_params_to_body_openai,
apply_model_system_prompt_to_body,
)
from open_webui.utils.system_prompts import remove_system_prompts_from_body
from open_webui.utils.auth import get_admin_user, get_verified_user
from open_webui.utils.access_control import has_access
from open_webui.utils.access_control import has_access, has_permission


from open_webui.config import (
Expand Down Expand Up @@ -1274,6 +1275,17 @@ async def generate_chat_completion(
bypass_filter = True

metadata = form_data.pop("metadata", None)

if (
not getattr(request.state, "system_prompt_permission_checked", False)
and user.role != "admin"
and not has_permission(
user.id, "chat.system_prompt", request.app.state.config.USER_PERMISSIONS
)
):
form_data = remove_system_prompts_from_body(form_data)
request.state.system_prompt_permission_checked = True

try:
form_data = GenerateChatCompletionForm(**form_data)
except Exception as e:
Expand Down Expand Up @@ -1459,6 +1471,16 @@ async def generate_openai_chat_completion(
):
metadata = form_data.pop("metadata", None)

if (
not getattr(request.state, "system_prompt_permission_checked", False)
and user.role != "admin"
and not has_permission(
user.id, "chat.system_prompt", request.app.state.config.USER_PERMISSIONS
)
):
form_data = remove_system_prompts_from_body(form_data)
request.state.system_prompt_permission_checked = True

try:
completion_form = OpenAIChatCompletionForm(**form_data)
except Exception as e:
Expand Down
13 changes: 12 additions & 1 deletion backend/open_webui/routers/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@
apply_model_params_to_body_openai,
apply_model_system_prompt_to_body,
)
from open_webui.utils.system_prompts import remove_system_prompts_from_body
from open_webui.utils.misc import (
convert_logit_bias_input_to_json,
)

from open_webui.utils.auth import get_admin_user, get_verified_user
from open_webui.utils.access_control import has_access
from open_webui.utils.access_control import has_access, has_permission


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -705,6 +706,16 @@ async def generate_chat_completion(
payload = {**form_data}
metadata = payload.pop("metadata", None)

if (
not getattr(request.state, "system_prompt_permission_checked", False)
and user.role != "admin"
and not has_permission(
user.id, "chat.system_prompt", request.app.state.config.USER_PERMISSIONS
)
):
payload = remove_system_prompts_from_body(payload)
request.state.system_prompt_permission_checked = True

model_id = form_data.get("model")
model_info = Models.get_model_by_id(model_id)

Expand Down
8 changes: 8 additions & 0 deletions backend/open_webui/routers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from open_webui.utils.auth import get_admin_user, get_password_hash, get_verified_user
from open_webui.utils.access_control import get_permissions, has_permission
from open_webui.utils.system_prompts import remove_system_prompts_from_settings


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -227,6 +228,13 @@ async def update_user_settings_by_session_user(
# If the user is not an admin and does not have permission to use tool servers, remove the key
updated_user_settings["ui"].pop("toolServers", None)

if user.role != "admin" and not has_permission(
user.id, "chat.system_prompt", request.app.state.config.USER_PERMISSIONS
):
updated_user_settings = remove_system_prompts_from_settings(
updated_user_settings
)

user = Users.update_user_settings_by_id(user.id, updated_user_settings)
if user:
return user.settings
Expand Down
52 changes: 52 additions & 0 deletions backend/open_webui/test/utils/test_payload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from open_webui.utils.system_prompts import (
remove_system_prompts_from_body,
remove_system_prompts_from_settings,
)


def test_remove_system_prompts_from_body_strips_user_controlled_system_fields():
body = {
"model": "test-model",
"messages": [
{"role": "system", "content": "do not forward"},
{"role": "user", "content": "hello"},
{"role": "assistant", "content": "hi"},
],
"system": "native ollama system",
"params": {
"system": "chat control system",
"temperature": 0.5,
},
"options": {
"system": "ollama option system",
"num_ctx": 4096,
},
}

sanitized = remove_system_prompts_from_body(body)

assert sanitized["messages"] == [
{"role": "user", "content": "hello"},
{"role": "assistant", "content": "hi"},
]
assert "system" not in sanitized
assert sanitized["params"] == {"temperature": 0.5}
assert sanitized["options"] == {"num_ctx": 4096}


def test_remove_system_prompts_from_settings_strips_persisted_system_fields():
settings = {
"system": "saved global prompt",
"params": {
"system": "saved control prompt",
"stream_response": True,
},
"ui": {"theme": "dark"},
}

sanitized = remove_system_prompts_from_settings(settings)

assert sanitized == {
"params": {"stream_response": True},
"ui": {"theme": "dark"},
}
2 changes: 2 additions & 0 deletions backend/open_webui/utils/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ async def generate_chat_completion(
bypass_filter: bool = False,
):
log.debug(f"generate_chat_completion: {form_data}")
request.state.system_prompt_permission_checked = True

if BYPASS_MODEL_ACCESS_CONTROL:
bypass_filter = True

Expand Down
39 changes: 39 additions & 0 deletions backend/open_webui/utils/system_prompts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
def remove_system_prompts_from_body(form_data: dict) -> dict:
if not isinstance(form_data, dict):
return form_data

messages = form_data.get("messages")
if isinstance(messages, list):
form_data["messages"] = [
message
for message in messages
if not (
isinstance(message, dict)
and str(message.get("role", "")).lower() == "system"
)
]

form_data.pop("system", None)

params = form_data.get("params")
if isinstance(params, dict):
params.pop("system", None)

options = form_data.get("options")
if isinstance(options, dict):
options.pop("system", None)

return form_data


def remove_system_prompts_from_settings(settings: dict) -> dict:
if not isinstance(settings, dict):
return settings

settings.pop("system", None)

params = settings.get("params")
if isinstance(params, dict):
params.pop("system", None)

return settings