Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
651a02e
i18n: add pt-BR translations for newly added UI items and consistency…
joaoback Jun 30, 2026
4067e35
refac
tjbck Jun 30, 2026
1014c21
i18n(th-TH): translate missing Thai keys/fix typo (#26406)
SNaytiP Jun 30, 2026
6ed56b0
refac
tjbck Jun 30, 2026
139206f
refac
tjbck Jun 30, 2026
0b75445
refac
tjbck Jul 1, 2026
af1c0ee
refac
tjbck Jul 1, 2026
58ef80d
refac
tjbck Jul 1, 2026
0b0eec0
refac
tjbck Jul 1, 2026
56ee875
refac
tjbck Jul 1, 2026
2ed8934
refac
tjbck Jul 1, 2026
9866a02
refac
tjbck Jul 1, 2026
2d7fc04
fix: use updated_at for sidebar chat timestamp (#26454)
Classic298 Jul 1, 2026
a78f0c0
fix: use absolute indexURL for pyodide sandbox (#26625)
Algorithm5838 Jul 1, 2026
980786f
i18n: fix Spanish relative time labels (#26463)
torgus Jul 1, 2026
9ed7af8
Update and fix Catalan translation.json (#26409)
aleixdorca Jul 1, 2026
19d8f03
refac
tjbck Jul 1, 2026
688bda0
refac
tjbck Jul 1, 2026
ab22fe6
refac
tjbck Jul 1, 2026
8fe4802
refac
tjbck Jul 1, 2026
989c6c1
refac
tjbck Jul 1, 2026
b6d4bae
refac
tjbck Jul 1, 2026
64b92ff
refac
tjbck Jul 1, 2026
650b817
refac
tjbck Jul 1, 2026
0016266
refac
tjbck Jul 1, 2026
4b08d65
refac
tjbck Jul 1, 2026
c416c6c
refac
tjbck Jul 1, 2026
9562f03
refac
tjbck Jul 1, 2026
645cd93
refac
tjbck Jul 1, 2026
73c0be8
refac
tjbck Jul 1, 2026
0e5540c
fix: derive content from output for search (#26405)
Algorithm5838 Jul 1, 2026
092b585
refac
tjbck Jul 1, 2026
c98d8ec
refac
tjbck Jul 1, 2026
fa2abe4
refac
tjbck Jul 1, 2026
4351c78
refac
tjbck Jul 1, 2026
52ee5cb
refac
tjbck Jul 1, 2026
54f31c6
refac
tjbck Jul 1, 2026
7f182ea
refac
tjbck Jul 1, 2026
80af65c
refac
tjbck Jul 1, 2026
2e5b094
refac
tjbck Jul 1, 2026
90eca2a
refac
tjbck Jul 1, 2026
4b55e69
Update CHANGELOG.md (#26641)
Classic298 Jul 1, 2026
ecd48e2
0.10.2 (#26642)
tjbck Jul 1, 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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ OPENAI_API_KEY=''
# CORS_ALLOW_ORIGIN='http://localhost:5173;http://localhost:8080'
CORS_ALLOW_ORIGIN='*'

# Set to false to keep memory tools enabled without adding memory context to the system context.
ENABLE_MEMORY_SYSTEM_CONTEXT=true

# For production you should set this to match the proxy configuration (127.0.0.1)
FORWARDED_ALLOW_IPS='*'

Expand Down
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github: tjbck
github: open-webui
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,45 @@ 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.10.2] - 2026-07-01

### Added

- 💭 **Streamed reasoning display.** Models that emit thinking or reasoning now show that content as it streams, and it renders correctly in the chat overview and in exported conversations. [Commit](https://github.com/open-webui/open-webui/commit/0b75445ff9a42e37640c034812d0de9b84039e60), [Commit](https://github.com/open-webui/open-webui/commit/af1c0eee89810fa4c36e3eb7e4eba6de685bd7ca), [Commit](https://github.com/open-webui/open-webui/commit/4b08d65597e5b634b7191b0bd6d28feeafcc2a48), [Commit](https://github.com/open-webui/open-webui/commit/fa2abe4cb6a085a4c9045bf8d8ff4b6beffdef6a)
- 🗂️ **Folder uploads to knowledge bases.** Dragging a folder into a knowledge base, or syncing one, now recreates its subfolder structure instead of flattening everything into loose files. [#26130](https://github.com/open-webui/open-webui/issues/26130), [Commit](https://github.com/open-webui/open-webui/commit/2ed8934f5b2bc8a11c74ef2f34cdb62ef024809e)
- 🧠 **Memory system context toggle.** Administrators can now keep memory tools available while choosing not to add stored memories to the system context, using the new 'Memory System Context' toggle in admin settings. [Commit](https://github.com/open-webui/open-webui/commit/4067e357b2ff9e2fb59866d24656e832908fb6fe)
- 🧹 **Tidier automatic memories.** Automatically saved memories now focus on enduring details like preferences and goals and skip one-off things like meals, routine events, or passing mood unless you ask to remember them. [Commit](https://github.com/open-webui/open-webui/commit/80af65c24adac5140a39a2b5687a3b669b86719f)
- 🎙️ **Speech-to-text request format.** OpenAI-compatible speech-to-text can now send audio as either a multipart upload or base64 JSON, selectable in admin audio settings. [Commit](https://github.com/open-webui/open-webui/commit/989c6c13f5d4c5cc255aec77aea14725104d9cb3)
- 🧰 **API configs via environment.** Administrators can now set per-connection Ollama and OpenAI API configurations through the "OLLAMA_API_CONFIGS" and "OPENAI_API_CONFIGS" environment variables. [Commit](https://github.com/open-webui/open-webui/commit/19d8f03bd2c64013b510f2a0eeb3513d452814a3)
- 📡 **Provider failure events.** Failed Ollama and OpenAI-compatible provider requests now emit a structured event describing the error type, provider, and status, giving administrators clearer visibility into upstream failures. [Commit](https://github.com/open-webui/open-webui/commit/4351c78b1e45bb0c5824f9d9eb911b395e343646)
- 🏟️ **Arena models via environment.** Administrators can now define evaluation arena models through the "EVALUATION_ARENA_MODELS" environment variable. [#26174](https://github.com/open-webui/open-webui/issues/26174)
- ♿ **Clearer high-contrast sidebar selection.** With high-contrast mode enabled, the currently selected chat in the sidebar now stands out with stronger colors, making it easier to tell which chat is active. [#26469](https://github.com/open-webui/open-webui/issues/26469), [Commit](https://github.com/open-webui/open-webui/commit/52ee5cb1b3aa8a8ad40b23d72d82db7c3522dbf8)
- 🔄 **General improvements.** Various improvements were implemented across the application to enhance performance, stability, and security.
- 🌐 **Translation updates.** Translations for Thai, Portuguese (Brazil), Catalan, and Spanish were enhanced and expanded.

### Fixed

- 🛡️ **Security Advisory**: This release includes security and access-control fixes. We recommend updating production deployments at your earliest convenience. Not all security fixes in this version may be enumerated in the fixed section — some may be withheld for a short time to give administrators time to upgrade. [Advisories](https://github.com/open-webui/open-webui/security)
- 🔐 **Fewer unexpected logouts.** A single request returning an authorization error no longer signs you out while your session is still valid, since the app now confirms the session status before redirecting to login. [Commit](https://github.com/open-webui/open-webui/commit/56ee875e21cb3b137a5aa1eca6eb3c0731b14045)
- 🔒 **Web search domain filtering.** Domain allow and block rules for web search results are now matched against the host, closing a gap where some URLs could slip past the filter. [Commit](https://github.com/open-webui/open-webui/commit/688bda09fbe26619613aa487beda8059a5fd55ef)
- 🕵️ **Image prompt log privacy.** Image generation workflows are no longer written to server logs at the default log level, keeping user-authored prompt content out of operator-visible logs. [#26400](https://github.com/open-webui/open-webui/issues/26400), [Commit](https://github.com/open-webui/open-webui/commit/64b92ff08a09b1bf338d5d95b7bf6f9db0d7e857)
- 🗄️ **Safer database upgrades.** Upgrading an existing SQLite database no longer crashes during the user-table migration or corrupts saved user settings, resolving failures that could block startup or break login after an upgrade. [#26403](https://github.com/open-webui/open-webui/issues/26403), [Commit](https://github.com/open-webui/open-webui/commit/c416c6cad69cc4ce44faacdad87cc65862625fb4)
- ⚙️ **Saving settings as a non-admin.** Non-admin users can once again save their interface settings, such as the default model and theme, which previously failed with a server error while the interface incorrectly reported success. [#26627](https://github.com/open-webui/open-webui/issues/26627), [Commit](https://github.com/open-webui/open-webui/commit/9866a02863c12c466100aed832f0006225759493)
- 🕒 **Sidebar chat timestamps.** Chats in the sidebar now show when they were last active instead of when they were created, so the time label matches their position in the list and refreshes after each new message. [#26454](https://github.com/open-webui/open-webui/pull/26454), [#26451](https://github.com/open-webui/open-webui/issues/26451)
- 🎯 **Default model after refresh.** Your selected model is no longer cleared when you reload the page, as model selection now waits for the model list to finish loading. [Commit](https://github.com/open-webui/open-webui/commit/b6d4baeb7ea9bc83e5486ca98b3cae07d3869aa7)
- ⏳ **Tool dialogs no longer hang.** Dismissing a tool or function input dialog by clicking outside it now cancels the pending request instead of leaving the chat spinning indefinitely. [#26417](https://github.com/open-webui/open-webui/issues/26417), [Commit](https://github.com/open-webui/open-webui/commit/0016266c0652757e32aa44f2e1fb7311ac08db51)
- 🐍 **Reliable code execution loading.** Running Python code in chat now loads its runtime reliably, fixing sandbox startup failures that broke code execution and the Pyodide file viewer in recent releases. [#26625](https://github.com/open-webui/open-webui/pull/26625), [#26390](https://github.com/open-webui/open-webui/issues/26390)
- 🤖 **Models with null capabilities.** Chatting with a model whose capabilities are unset no longer fails with an error when the memory feature or automations are involved. [#26412](https://github.com/open-webui/open-webui/issues/26412), [Commit](https://github.com/open-webui/open-webui/commit/650b81792582268345073ce3757ac14356189d75), [Commit](https://github.com/open-webui/open-webui/commit/0016266c0652757e32aa44f2e1fb7311ac08db51)
- 🔎 **Searchable responses.** Chat search again finds assistant messages whose text is stored as structured output, which were previously skipped. [#26405](https://github.com/open-webui/open-webui/pull/26405)
- 🔔 **Chat notification previews.** Background chat completion notifications and toasts now show a clean response preview instead of appearing blank for messages stored as structured output. [Commit](https://github.com/open-webui/open-webui/commit/c98d8ecaccc7284573b050e88b3857c4aeba3860)
- 💾 **Banner and config startup.** Setting configuration such as "WEBUI_BANNERS" no longer causes a startup failure, since admin configuration values are now stored correctly regardless of their data type. [#26431](https://github.com/open-webui/open-webui/issues/26431), [Commit](https://github.com/open-webui/open-webui/commit/ab22fe64bdd10ba86845dcd46dfd7f619b22366f)
- 📑 **RAG Template visibility.** The RAG Template editor now stays visible in admin document settings even when Bypass Embedding and Retrieval is enabled, since the template still applies to document content in that mode. [#26126](https://github.com/open-webui/open-webui/issues/26126), [Commit](https://github.com/open-webui/open-webui/commit/8fe480250f649824441a7b4d725a502683134713)
- 🧬 **Editing derived models.** Editing a workspace model no longer clears its base model, including when that base is a preset or the model itself. [Commit](https://github.com/open-webui/open-webui/commit/092b5857bbda01be53619e26f42b1fa9b64a2b44), [Commit](https://github.com/open-webui/open-webui/commit/54f31c630afa75e9c81acaa048e1bd901254ba92)

### 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.10.1] - 2026-06-29

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
![GitHub top language](https://img.shields.io/github/languages/top/open-webui/open-webui)
![GitHub last commit](https://img.shields.io/github/last-commit/open-webui/open-webui?color=red)
[![Discord](https://img.shields.io/badge/Discord-Open_WebUI-blue?logo=discord&logoColor=white)](https://discord.gg/5rJgQTnV4s)
[![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/tjbck)
[![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/open-webui)

![Open WebUI Banner](./banner.png)

Expand Down
25 changes: 25 additions & 0 deletions backend/open_webui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,16 @@ def reachable(host: str, port: int) -> bool:
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS

OLLAMA_API_CONFIGS = {}
_ollama_api_configs = os.getenv('OLLAMA_API_CONFIGS', '')
if _ollama_api_configs:
try:
parsed = json.loads(_ollama_api_configs)
if isinstance(parsed, dict):
OLLAMA_API_CONFIGS = parsed
else:
log.warning('OLLAMA_API_CONFIGS must be a JSON object, ignoring')
except (json.JSONDecodeError, TypeError):
log.warning('OLLAMA_API_CONFIGS is not valid JSON, ignoring')

####################################
# OPENAI_API
Expand Down Expand Up @@ -327,6 +337,16 @@ def reachable(host: str, port: int) -> bool:
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS

OPENAI_API_CONFIGS = {}
_openai_api_configs = os.getenv('OPENAI_API_CONFIGS', '')
if _openai_api_configs:
try:
parsed = json.loads(_openai_api_configs)
if isinstance(parsed, dict):
OPENAI_API_CONFIGS = parsed
else:
log.warning('OPENAI_API_CONFIGS must be a JSON object, ignoring')
except (json.JSONDecodeError, TypeError):
log.warning('OPENAI_API_CONFIGS is not valid JSON, ignoring')

# Get the actual OpenAI API key based on the base URL
OPENAI_API_KEY = ''
Expand Down Expand Up @@ -394,6 +414,7 @@ def reachable(host: str, port: int) -> bool:
ENABLE_CODE_INTERPRETER = os.getenv('ENABLE_CODE_INTERPRETER', 'True').lower() == 'true'

ENABLE_MEMORIES = os.getenv('ENABLE_MEMORIES', 'True').lower() == 'true'
ENABLE_MEMORY_SYSTEM_CONTEXT = os.getenv('ENABLE_MEMORY_SYSTEM_CONTEXT', 'True').lower() == 'true'
ENABLE_MEMORY_BACKGROUND_REVIEW = os.getenv('ENABLE_MEMORY_BACKGROUND_REVIEW', 'False').lower() == 'true'
MEMORIES_REVIEW_INTERVAL_TURNS = int(os.getenv('MEMORIES_REVIEW_INTERVAL_TURNS', '10'))
MEMORIES_USER_CHAR_LIMIT = int(os.getenv('MEMORIES_USER_CHAR_LIMIT', '2000'))
Expand Down Expand Up @@ -1511,6 +1532,8 @@ def reachable(host: str, port: int) -> bool:

AUDIO_STT_OPENAI_API_KEY = os.getenv('AUDIO_STT_OPENAI_API_KEY', OPENAI_API_KEY)

AUDIO_STT_OPENAI_API_REQUEST_FORMAT = os.getenv('AUDIO_STT_OPENAI_API_REQUEST_FORMAT', 'multipart')

AUDIO_STT_ENGINE = os.getenv('AUDIO_STT_ENGINE', '')

AUDIO_STT_MODEL = os.getenv('AUDIO_STT_MODEL', '')
Expand Down Expand Up @@ -2741,6 +2764,7 @@ def feishu_oauth_register(oauth: OAuth):
'code_execution.jupyter.timeout': CODE_EXECUTION_JUPYTER_TIMEOUT,
'code_interpreter.enable': ENABLE_CODE_INTERPRETER,
'memories.enable': ENABLE_MEMORIES,
'memories.system_context.enable': ENABLE_MEMORY_SYSTEM_CONTEXT,
'memories.background_review.enable': ENABLE_MEMORY_BACKGROUND_REVIEW,
'memories.review_interval_turns': MEMORIES_REVIEW_INTERVAL_TURNS,
'memories.user_char_limit': MEMORIES_USER_CHAR_LIMIT,
Expand Down Expand Up @@ -2944,6 +2968,7 @@ def feishu_oauth_register(oauth: OAuth):
'audio.stt.deepgram.api_key': DEEPGRAM_API_KEY,
'audio.stt.openai.api_base_url': AUDIO_STT_OPENAI_API_BASE_URL,
'audio.stt.openai.api_key': AUDIO_STT_OPENAI_API_KEY,
'audio.stt.openai.api_request_format': AUDIO_STT_OPENAI_API_REQUEST_FORMAT,
'audio.stt.engine': AUDIO_STT_ENGINE,
'audio.stt.model': AUDIO_STT_MODEL,
'audio.stt.supported_content_types': AUDIO_STT_SUPPORTED_CONTENT_TYPES,
Expand Down
11 changes: 5 additions & 6 deletions backend/open_webui/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,17 +444,16 @@ def parse_section(section):


if WEBSOCKET_REDIS_OPTIONS == '':
WEBSOCKET_REDIS_OPTIONS = {'socket_timeout': None}
if REDIS_SOCKET_CONNECT_TIMEOUT:
WEBSOCKET_REDIS_OPTIONS = {'socket_connect_timeout': REDIS_SOCKET_CONNECT_TIMEOUT}
else:
log.debug('No WEBSOCKET_REDIS_OPTIONS provided, defaulting to None')
WEBSOCKET_REDIS_OPTIONS = None
WEBSOCKET_REDIS_OPTIONS['socket_connect_timeout'] = REDIS_SOCKET_CONNECT_TIMEOUT
else:
try:
WEBSOCKET_REDIS_OPTIONS = json.loads(WEBSOCKET_REDIS_OPTIONS)
WEBSOCKET_REDIS_OPTIONS.setdefault('socket_timeout', None)
except Exception:
log.warning('Invalid WEBSOCKET_REDIS_OPTIONS, defaulting to None')
WEBSOCKET_REDIS_OPTIONS = None
log.warning('Invalid WEBSOCKET_REDIS_OPTIONS, defaulting to socket_timeout=None')
WEBSOCKET_REDIS_OPTIONS = {'socket_timeout': None}

WEBSOCKET_REDIS_URL = os.getenv('WEBSOCKET_REDIS_URL', REDIS_URL)
WEBSOCKET_REDIS_CLUSTER = os.getenv('WEBSOCKET_REDIS_CLUSTER', str(REDIS_CLUSTER)).lower() == 'true'
Expand Down
62 changes: 62 additions & 0 deletions backend/open_webui/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ class EventDefinitions(BaseModel):
description='Model provider configuration was updated.',
message='Model Provider Config updated',
)
MODEL_PROVIDER_REQUEST_FAILED: EventDefinition = EventDefinition(
name='model.provider_request.failed',
description='A model provider request failed.',
message='Model provider request failed',
)
MODEL_PROVIDER_MODEL_CREATED: EventDefinition = EventDefinition(
name='model.provider_model.created',
description='A provider model was created.',
Expand Down Expand Up @@ -1108,3 +1113,60 @@ async def publish_event(
await sink.handle_event(app, event_payload, request=request)
except Exception:
log.exception('Event sink failed for %s', event_payload.event)


async def publish_model_provider_request_failed(
request_or_app: Any,
*,
actor: Any | None,
provider: str,
base_url: str,
status: int,
requested_model: str | None = None,
api_key: str | None = None,
upstream_error: Any = None,
) -> None:
error = upstream_error.get('error') if isinstance(upstream_error, dict) else upstream_error
error_code = None
if isinstance(error, dict):
error_code = error.get('code') or error.get('type') or error.get('error_code')
error = error.get('message') or error.get('detail') or error

error_text = str(error or '')
marker = f'{error_code or ""} {error_text}'.lower()
error_type = (
'model_not_found'
if status == 404
and any(value in marker for value in ('model_not_found', 'model not found', 'does not exist', 'no such model'))
else 'authentication_failed'
if status in (401, 403)
else 'rate_limited'
if status == 429
else 'server_failed'
if status >= 500
else 'upstream_error'
)

data = {
'error_type': error_type,
'status': status,
'provider': provider,
'base_url': base_url,
}
if requested_model:
data['requested_model'] = requested_model
if api_key:
data['api_key_suffix'] = f'...{api_key[-4:]}'
if error_code:
data['upstream_error_code'] = error_code
if error:
data['upstream_message'] = error

await publish_event(
request_or_app,
EVENTS.MODEL_PROVIDER_REQUEST_FAILED,
actor=actor,
subject_id=requested_model,
subject_type='model',
data=data,
)
16 changes: 12 additions & 4 deletions backend/open_webui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1341,18 +1341,26 @@ async def run_initial_title_generation():
detail=ERROR_MESSAGES.DEFAULT(),
)

# Persist chat-level files (knowledge collections, docs, etc.)
user_message = metadata.get('user_message') or {}
selected_chat_models = user_message.get('models') if isinstance(user_message, dict) else None
if not isinstance(selected_chat_models, list) or not selected_chat_models:
selected_chat_models = [entry.get('model_id') for entry in message_ids if entry.get('model_id')]

# Persist chat-level fields the frontend used to save on every message.
# The old frontend saveChatHandler did this on every message;
# now the backend owns persistence.
chat_files = metadata.get('files')
if chat_files is not None:
if chat_files is not None or selected_chat_models:
existing_chat = await Chats.get_chat_by_id(chat_id)
if existing_chat:
updated = {**existing_chat.chat, 'files': chat_files}
updated = {**existing_chat.chat}
if chat_files is not None:
updated['files'] = chat_files
if selected_chat_models:
updated['models'] = selected_chat_models
await Chats.update_chat_by_id(chat_id, updated)

# Save user message to DB
user_message = metadata.get('user_message') or {}
if user_message and user_message.get('id'):
await Chats.upsert_message_to_chat_by_id_and_message_id(
chat_id,
Expand Down
Loading
Loading