Skip to content
Merged

Dev #264

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
49e7ead
Merge pull request #21345 from joaoback/patch-25
joaoback Feb 13, 2026
0b05b2f
refac
tjbck Feb 13, 2026
9886ebb
fix: resolve knowledge collection indentation/truncation issue by cor…
silentoplayz Feb 13, 2026
4d5b7b3
fix: resolve knowledge tooltip z-index issue in model edit page (#21375)
silentoplayz Feb 13, 2026
ddcec98
fix: ensure sync modal only triggers if community sharing is enabled …
silentoplayz Feb 13, 2026
7bda6bf
fix: PostgreSQL cannot use get_chat_ids_by_model_id
tjbck Feb 13, 2026
73776d5
fix: enforce public sharing permission checks across all resource typ…
Classic298 Feb 13, 2026
b4c3f54
fix: skills postgres issue
tjbck Feb 13, 2026
df6e380
refac
tjbck Feb 13, 2026
d01b1d4
enh: apply admin default to builtin web search (#21373)
Classic298 Feb 13, 2026
97a3b15
Update utils.py (#21105)
Classic298 Feb 13, 2026
ca6b18a
refac: is_user_active
tjbck Feb 13, 2026
20de5a8
refac
tjbck Feb 13, 2026
589c4e6
refac
tjbck Feb 13, 2026
b7549d2
refac: defer profile
tjbck Feb 13, 2026
d1d1efe
refac: scim
tjbck Feb 13, 2026
370a677
fix: pin torch to prevent startup errors on ARM devices (#21385)
Classic298 Feb 13, 2026
f027a01
fix: direct model access control
tjbck Feb 13, 2026
163211a
refac: styling
tjbck Feb 13, 2026
2487c84
refac: styling
tjbck Feb 13, 2026
64fa26b
refac
tjbck Feb 13, 2026
abc9b63
refac
tjbck Feb 13, 2026
0f3f68b
enh (#21362)
Classic298 Feb 13, 2026
a9b8677
refac
tjbck Feb 13, 2026
79ecbfc
refac
tjbck Feb 13, 2026
626d236
chore: format
tjbck Feb 13, 2026
b36f8d9
chore: format
tjbck Feb 13, 2026
e5d88be
doc: changelog
tjbck Feb 13, 2026
3b61562
refac
tjbck Feb 13, 2026
d33ad46
refac
tjbck Feb 13, 2026
a30b106
fix issues/21399 (#21400)
taylorwilsdon Feb 13, 2026
5de60dc
refac
tjbck Feb 13, 2026
12bad45
chore: Changelog updates (#21382)
Classic298 Feb 13, 2026
883f1dd
Merge pull request #21346 from open-webui/dev
tjbck Feb 14, 2026
a38298f
Merge remote-tracking branch 'openwebui/main' into dev
OrenZhang Feb 14, 2026
ceaefb6
refactor(openai): remove unused responses endpoint
OrenZhang Feb 14, 2026
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
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.8.1] - 2026-02-14

### Added

- 🚀 **Channel user active status.** Checking user active status in channels is now faster thanks to optimized database queries. [Commit](https://github.com/open-webui/open-webui/commit/ca6b18ab5cb94153a9dae233f975d36bf6b19b76)
- 🔗 **Responses API endpoint with model routing.** The OpenAI API proxy now supports a /responses endpoint that routes requests to the correct backend based on the model field in the request, instead of always using the first configured endpoint. This enables support for backends like vLLM that provide /skills and /v1/responses endpoints. [Commit](https://github.com/open-webui/open-webui/commit/abc9b63093d65f4d74342db85b7d5df1809aa0f0), [Commit](https://github.com/open-webui/open-webui/commit/79ecbfc757f0642740d0e44fab98263d84295490)
- ⚡ **Model and prompt list optimization.** Improved performance when loading models and prompts by pre-fetching user group IDs once instead of making multiple database queries. [Commit](https://github.com/open-webui/open-webui/commit/20de5a87da0c12e4052b50887a42ddd7228c5ef5)
- 🗄️ **Batch access control queries.** Improved performance when loading models, prompts, and knowledge bases by replacing multiple individual access checks with single batch queries, significantly reducing database load for large deployments. [Commit](https://github.com/open-webui/open-webui/commit/589c4e64c1b7bb7a7a5abc20382b92fb860e28c2)
- 💨 **Faster user list loading.** User lists now load significantly faster by deferring profile image loading; images are fetched separately in parallel by the browser, improving caching and reducing database load. [Commit](https://github.com/open-webui/open-webui/commit/b7549d2f6ca2843661ec79a5a1e55da9e7553368)
- 🔍 **Web search result count.** The built-in search_web tool now respects the admin-configured "Search Result Count" setting instead of always returning 5 results when using Native Function Calling mode. [#21373](https://github.com/open-webui/open-webui/pull/21373), [#21371](https://github.com/open-webui/open-webui/issues/21371)
- 🔐 **SCIM externalId support.** SCIM-enabled deployments can now store and manage externalId for user provisioning, enabling better integration with identity providers like Microsoft Entra ID and Okta. [#21099](https://github.com/open-webui/open-webui/pull/21099), [#21280](https://github.com/open-webui/open-webui/issues/21280), [Commit](https://github.com/open-webui/open-webui/commit/d1d1efe212b16e0052359991d67fd813125077e8)
- 🌐 **Translation updates.** Portuguese (Brazil) translations were updated.

### Fixed

- 🛡️ **Public sharing security fix.** Fixed a security issue where users with write access could see the Public sharing option regardless of their actual public sharing permission, and direct API calls could bypass frontend sharing restrictions. [#21358](https://github.com/open-webui/open-webui/pull/21358), [#21356](https://github.com/open-webui/open-webui/issues/21356)
- 🔒 **Direct model access control fix.** Model access control changes now persist correctly for direct Ollama and OpenAI models that don't have database entries, and error messages display properly instead of showing "[object Object]". [Commit](https://github.com/open-webui/open-webui/commit/f027a01ab2ff3b6175af3dd13a4478c265c0544a), [#21377](https://github.com/open-webui/open-webui/issues/21377)
- 💭 **Reasoning trace rendering performance.** Reasoning traces from models now render properly without being split into many fragments, preventing browser slowdowns during streaming responses. [#21348](https://github.com/open-webui/open-webui/issues/21348), [Commit](https://github.com/open-webui/open-webui/commit/3b61562c82448cf83710d8b6ed29b797991aa83a)
- 🖥️ **ARM device compatibility fix.** Fixed an issue where upgrading to 0.8.0 would fail to start on ARM devices (like Raspberry Pi 4) due to torch 2.10.0 causing SIGILL errors; now pinned to torch<=2.9.1. [#21385](https://github.com/open-webui/open-webui/pull/21385), [#21349](https://github.com/open-webui/open-webui/issues/21349)
- 🗄️ **Skills PostgreSQL compatibility fix.** Fixed a PostgreSQL compatibility issue where creating or listing skills would fail with a TypeError, while SQLite worked correctly. [#21372](https://github.com/open-webui/open-webui/pull/21372), [Commit](https://github.com/open-webui/open-webui/commit/b4c3f54f9648c4232a0fd6557703ffa66fcf4caa), [#21365](https://github.com/open-webui/open-webui/issues/21365)
- 🗄️ **PostgreSQL analytics query fix.** Fixed an issue where retrieving chat IDs by model ID would fail on PostgreSQL due to incompatible DISTINCT ordering, while SQLite worked correctly. [#21347](https://github.com/open-webui/open-webui/issues/21347), [Commit](https://github.com/open-webui/open-webui/commit/7bda6bf767d5d5c4dc1111465096a88e10b5030e)
- 🗃️ **SQLite cascade delete fix.** Deleting chats now properly removes all associated messages in SQLite, matching PostgreSQL behavior and preventing orphaned data. [#21362](https://github.com/open-webui/open-webui/pull/21362)
- ☁️ **Ollama Cloud model naming fix.** Fixed an issue where using Ollama Cloud models would fail with "Model not found" errors because ":latest" was incorrectly appended to model names. [#21386](https://github.com/open-webui/open-webui/issues/21386)
- 🛠️ **Knowledge selector tooltip z-index.** Fixed an issue where tooltips in the "Select Knowledge" dropdown were hidden behind the menu, making it difficult to read knowledge item names and descriptions. [#21375](https://github.com/open-webui/open-webui/pull/21375)
- 🎯 **Model selector scroll position.** The model selector dropdown now correctly scrolls to and centers the currently selected model when opened, and resets scroll position when reopened. [Commit](https://github.com/open-webui/open-webui/commit/0b05b2fc7ed4c38af158707438ff404d1beb7c91)
- 🐛 **Sync modal unexpected appearance.** Fixed an issue where the Sync Modal would appear unexpectedly after enabling the "Community Sharing" feature if the user had previously visited the app with the sync parameter. [#21376](https://github.com/open-webui/open-webui/pull/21376)
- 🎨 **Knowledge collection layout fix.** Fixed a layout issue in the Knowledge integration menu where long collection names caused indentation artifacts and now properly truncate with ellipsis. [#21374](https://github.com/open-webui/open-webui/pull/21374)
- 📝 **Metadata processing crash fix.** Fixed a latent bug where processing document metadata containing certain keys (content, pages, tables, paragraphs, sections, figures) would cause a RuntimeError due to dictionary mutation during iteration. [#21105](https://github.com/open-webui/open-webui/pull/21105)
- 🔑 **Password validation regex fix.** Fixed the password validation regex by adding the raw string prefix, ensuring escape sequences like \d and \w are interpreted correctly. [#21400](https://github.com/open-webui/open-webui/pull/21400), [#21399](https://github.com/open-webui/open-webui/issues/21399)

### Changed

- ⚠️ **Database Migrations:** This release includes database schema changes; we strongly recommend backing up your database and all associated data before upgrading in production environments. If you are running a multi-worker, multi-server, or load-balanced deployment, all instances must be updated simultaneously, rolling updates are not supported and will cause application failures due to schema incompatibility.

## [0.8.0] - 2026-02-12

### Added
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG_EXTRA.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.8.1.1] - 2026.02.14

### Changed

- 合并官方 0.8.1 改动
- 移除 Responses 接口

## [0.8.0.2] - 2026.02.13

### Fixed
Expand Down
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,16 @@ COPY --chown=$UID:$GID ./backend/requirements.txt ./requirements.txt
RUN pip3 install --no-cache-dir uv && \
if [ "$USE_CUDA" = "true" ]; then \
# If you use CUDA the whisper and embedding model will be downloaded on first use
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/$USE_CUDA_DOCKER_VER --no-cache-dir && \
# fix: pin torch<=2.9.1 - torch 2.10.0 aarch64 wheels cause SIGILL on ARM devices (RPi 4 Cortex-A72) #21349
pip3 install 'torch<=2.9.1' torchvision torchaudio --index-url https://download.pytorch.org/whl/$USE_CUDA_DOCKER_VER --no-cache-dir && \
uv pip install --system -r requirements.txt --no-cache-dir && \
python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ.get('AUXILIARY_EMBEDDING_MODEL', 'TaylorAI/bge-micro-v2'), device='cpu')" && \
python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \
python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
python -c "import nltk; nltk.download('punkt_tab')"; \
else \
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir && \
pip3 install 'torch<=2.9.1' torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir && \
uv pip install --system -r requirements.txt --no-cache-dir && \
if [ "$USE_SLIM" != "true" ]; then \
python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
Expand Down
10 changes: 9 additions & 1 deletion backend/open_webui/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ def parse_section(section):
)
PASSWORD_VALIDATION_REGEX_PATTERN = os.environ.get(
"PASSWORD_VALIDATION_REGEX_PATTERN",
"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$",
r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$",
)


Expand Down Expand Up @@ -574,6 +574,14 @@ def parse_section(section):
== "true"
)
SCIM_TOKEN = os.environ.get("SCIM_TOKEN", "")
SCIM_AUTH_PROVIDER = os.environ.get("SCIM_AUTH_PROVIDER", "")

if ENABLE_SCIM and not SCIM_AUTH_PROVIDER:
log.warning(
"SCIM is enabled but SCIM_AUTH_PROVIDER is not set. "
"Set SCIM_AUTH_PROVIDER to the OAuth provider name (e.g. 'microsoft', 'oidc') "
"to enable externalId storage."
)

####################################
# LICENSE_KEY
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""add scim column to user table

Revision ID: b2c3d4e5f6a7
Revises: a1b2c3d4e5f6
Create Date: 2026-02-13 14:19:00.000000

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = "b2c3d4e5f6a7"
down_revision: Union[str, None] = "a1b2c3d4e5f6"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.add_column("user", sa.Column("scim", sa.JSON(), nullable=True))


def downgrade() -> None:
op.drop_column("user", "scim")
56 changes: 56 additions & 0 deletions backend/open_webui/models/access_grants.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,62 @@ def has_access(
)
return exists is not None

def get_accessible_resource_ids(
self,
user_id: str,
resource_type: str,
resource_ids: list[str],
permission: str = "read",
user_group_ids: Optional[set[str]] = None,
db: Optional[Session] = None,
) -> set[str]:
"""
Batch check: return the subset of resource_ids that the user can access.

This replaces calling has_access() in a loop (N+1) with a single query.
"""
if not resource_ids:
return set()

with get_db_context(db) as db:
conditions = [
and_(
AccessGrant.principal_type == "user",
AccessGrant.principal_id == "*",
),
and_(
AccessGrant.principal_type == "user",
AccessGrant.principal_id == user_id,
),
]

if user_group_ids is None:
from open_webui.models.groups import Groups

user_groups = Groups.get_groups_by_member_id(user_id, db=db)
user_group_ids = {group.id for group in user_groups}

if user_group_ids:
conditions.append(
and_(
AccessGrant.principal_type == "group",
AccessGrant.principal_id.in_(user_group_ids),
)
)

rows = (
db.query(AccessGrant.resource_id)
.filter(
AccessGrant.resource_type == resource_type,
AccessGrant.resource_id.in_(resource_ids),
AccessGrant.permission == permission,
or_(*conditions),
)
.distinct()
.all()
)
return {row[0] for row in rows}

def get_users_with_access(
self,
resource_type: str,
Expand Down
16 changes: 9 additions & 7 deletions backend/open_webui/models/chat_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Text,
JSON,
Index,
func,
)

####################
Expand Down Expand Up @@ -279,25 +280,26 @@ def get_chat_ids_by_model_id(
db: Optional[Session] = None,
) -> list[str]:
"""Get distinct chat_ids that used a specific model."""
from sqlalchemy import distinct

with get_db_context(db) as db:
query = db.query(distinct(ChatMessage.chat_id)).filter(
ChatMessage.model_id == model_id
)
query = db.query(
ChatMessage.chat_id,
func.max(ChatMessage.created_at).label("last_message_at"),
).filter(ChatMessage.model_id == model_id)
if start_date:
query = query.filter(ChatMessage.created_at >= start_date)
if end_date:
query = query.filter(ChatMessage.created_at <= end_date)

# Order by most recent message in each chat
# Group by chat_id and order by most recent message in each chat
chat_ids = (
query.order_by(ChatMessage.created_at.desc())
query.group_by(ChatMessage.chat_id)
.order_by(func.max(ChatMessage.created_at).desc())
.offset(skip)
.limit(limit)
.all()
)
return [chat_id for (chat_id,) in chat_ids]
return [chat_id for chat_id, _ in chat_ids]

def delete_messages_by_chat_id(
self, chat_id: str, db: Optional[Session] = None
Expand Down
34 changes: 33 additions & 1 deletion backend/open_webui/models/chats.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from open_webui.internal.db import Base, JSONField, get_db, get_db_context
from open_webui.models.tags import TagModel, Tag, Tags
from open_webui.models.folders import Folders
from open_webui.models.chat_messages import ChatMessages
from open_webui.models.chat_messages import ChatMessage, ChatMessages
from open_webui.utils.misc import sanitize_data_for_db, sanitize_text_for_db

from pydantic import BaseModel, ConfigDict
Expand Down Expand Up @@ -621,6 +621,13 @@ def delete_shared_chat_by_chat_id(
) -> bool:
try:
with get_db_context(db) as db:
# Use subquery to delete chat_messages for shared chats
shared_chat_id_subquery = (
db.query(Chat.id).filter_by(user_id=f"shared-{chat_id}").subquery()
)
db.query(ChatMessage).filter(
ChatMessage.chat_id.in_(shared_chat_id_subquery)
).delete(synchronize_session=False)
db.query(Chat).filter_by(user_id=f"shared-{chat_id}").delete()
db.commit()

Expand Down Expand Up @@ -1410,6 +1417,7 @@ def delete_all_tags_by_id_and_user_id(
def delete_chat_by_id(self, id: str, db: Optional[Session] = None) -> bool:
try:
with get_db_context(db) as db:
db.query(ChatMessage).filter_by(chat_id=id).delete()
db.query(Chat).filter_by(id=id).delete()
db.commit()

Expand All @@ -1422,6 +1430,7 @@ def delete_chat_by_id_and_user_id(
) -> bool:
try:
with get_db_context(db) as db:
db.query(ChatMessage).filter_by(chat_id=id).delete()
db.query(Chat).filter_by(id=id, user_id=user_id).delete()
db.commit()

Expand All @@ -1436,6 +1445,12 @@ def delete_chats_by_user_id(
with get_db_context(db) as db:
self.delete_shared_chats_by_user_id(user_id, db=db)

chat_id_subquery = (
db.query(Chat.id).filter_by(user_id=user_id).subquery()
)
db.query(ChatMessage).filter(
ChatMessage.chat_id.in_(chat_id_subquery)
).delete(synchronize_session=False)
db.query(Chat).filter_by(user_id=user_id).delete()
db.commit()

Expand All @@ -1448,6 +1463,14 @@ def delete_chats_by_user_id_and_folder_id(
) -> bool:
try:
with get_db_context(db) as db:
chat_id_subquery = (
db.query(Chat.id)
.filter_by(user_id=user_id, folder_id=folder_id)
.subquery()
)
db.query(ChatMessage).filter(
ChatMessage.chat_id.in_(chat_id_subquery)
).delete(synchronize_session=False)
db.query(Chat).filter_by(user_id=user_id, folder_id=folder_id).delete()
db.commit()

Expand Down Expand Up @@ -1481,6 +1504,15 @@ def delete_shared_chats_by_user_id(
chats_by_user = db.query(Chat).filter_by(user_id=user_id).all()
shared_chat_ids = [f"shared-{chat.id}" for chat in chats_by_user]

# Use subquery to delete chat_messages for shared chats
shared_id_subq = (
db.query(Chat.id)
.filter(Chat.user_id.in_(shared_chat_ids))
.subquery()
)
db.query(ChatMessage).filter(
ChatMessage.chat_id.in_(shared_id_subq)
).delete(synchronize_session=False)
db.query(Chat).filter(Chat.user_id.in_(shared_chat_ids)).delete()
db.commit()

Expand Down
6 changes: 3 additions & 3 deletions backend/open_webui/models/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
from typing import Optional

from sqlalchemy.orm import Session
from open_webui.internal.db import Base, JSONField, get_db, get_db_context
from open_webui.internal.db import Base, get_db, get_db_context
from open_webui.models.users import Users, UserResponse
from open_webui.models.groups import Groups
from open_webui.models.access_grants import AccessGrantModel, AccessGrants

from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy import BigInteger, Boolean, Column, String, Text, or_
from sqlalchemy import JSON, BigInteger, Boolean, Column, String, Text, or_

log = logging.getLogger(__name__)

Expand All @@ -26,7 +26,7 @@ class Skill(Base):
name = Column(Text, unique=True)
description = Column(Text, nullable=True)
content = Column(Text)
meta = Column(JSONField)
meta = Column(JSON)
is_active = Column(Boolean, default=True)

updated_at = Column(BigInteger)
Expand Down
Loading
Loading