From f81d0f6a202de33b54756bdc7534e3cc3130cbd1 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Tue, 3 Nov 2026 19:28:25 +0100 Subject: [PATCH 1/3] feat: optional size limit for chat history tool results Reading a long earlier chat through the Chat History tools could return far more text than a model's context window holds, so the next request failed. Admins can now set VIEW_CHAT_MAX_CHARS to cap what is returned. The newest messages are kept, older ones are dropped, and the result says it was cut and how many messages came back. Unset keeps the full chat as before. Fixes #33608 --- backend/open_webui/env.py | 2 ++ backend/open_webui/tools/builtin.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index b672fe0483..cdcbec7a66 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -1176,6 +1176,8 @@ KNOWLEDGE_GREP_MAX_MATCHES = _int_env('KNOWLEDGE_GREP_MAX_MATCHES', 50) # Characters returned by view_file / view_knowledge_file. VIEW_FILE_MAX_CHARS = _int_env('VIEW_FILE_MAX_CHARS', 100_000) VIEW_FILE_DEFAULT_MAX_CHARS = _int_env('VIEW_FILE_DEFAULT_MAX_CHARS', 10_000) +# Characters returned by view_chat; unset returns the full chat. +VIEW_CHAT_MAX_CHARS = int(os.getenv('VIEW_CHAT_MAX_CHARS')) if os.getenv('VIEW_CHAT_MAX_CHARS') else None # Tools/functions pip options diff --git a/backend/open_webui/tools/builtin.py b/backend/open_webui/tools/builtin.py index 1704b9d4d1..420d1397d1 100644 --- a/backend/open_webui/tools/builtin.py +++ b/backend/open_webui/tools/builtin.py @@ -16,6 +16,7 @@ from fastapi import HTTPException, Request from open_webui.env import ( KNOWLEDGE_GREP_MAX_MATCHES, + VIEW_CHAT_MAX_CHARS, VIEW_FILE_DEFAULT_MAX_CHARS, VIEW_FILE_MAX_CHARS, ) @@ -1750,6 +1751,23 @@ async def view_chat( ) current_id = msg.get('parentId') if msg else None + truncation = {} + if VIEW_CHAT_MAX_CHARS and sum(len(message['content']) for message in messages) > VIEW_CHAT_MAX_CHARS: + remaining_chars = VIEW_CHAT_MAX_CHARS + kept_messages = [] + for message in messages: + if remaining_chars <= 0: + break + content = message['content'][:remaining_chars] + kept_messages.append({**message, 'content': content}) + remaining_chars -= len(content) + truncation = { + 'truncated': True, + 'total_messages': len(messages), + 'returned_messages': len(kept_messages), + } + messages = kept_messages + # Reverse to get chronological order messages.reverse() @@ -1760,6 +1778,7 @@ async def view_chat( 'messages': messages, 'updated_at': chat.updated_at, 'created_at': chat.created_at, + **truncation, }, ensure_ascii=False, ) From 4e21ddc8d85f679470b57760114938c175c6e14d Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Tue, 3 Nov 2026 19:43:34 +0100 Subject: [PATCH 2/3] feat: set the chat read limit from the admin settings The limit on how much of an earlier chat a model can read could only be set with an environment variable. It is now a Chat Read Limit field in Admin Settings under General, next to the skill limits. The environment variable still works as the starting value, and the setting saved in the panel takes precedence. --- backend/open_webui/config.py | 5 +++++ backend/open_webui/env.py | 2 -- backend/open_webui/routers/auths.py | 4 ++++ backend/open_webui/tools/builtin.py | 6 +++--- src/lib/components/admin/Settings/General.svelte | 16 ++++++++++++++++ src/lib/i18n/locales/en-US/translation.json | 2 ++ 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 3e98856ec4..b934887edd 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -2067,6 +2067,10 @@ SKILL_PREVIEW_MAX_CHARS = int(os.environ['SKILL_PREVIEW_MAX_CHARS']) if os.geten if SKILL_PREVIEW_MAX_CHARS is not None and SKILL_PREVIEW_MAX_CHARS <= 0: raise ValueError('SKILL_PREVIEW_MAX_CHARS must be a positive integer or unset') +VIEW_CHAT_MAX_CHARS = int(os.environ['VIEW_CHAT_MAX_CHARS']) if os.getenv('VIEW_CHAT_MAX_CHARS') else None +if VIEW_CHAT_MAX_CHARS is not None and VIEW_CHAT_MAX_CHARS <= 0: + raise ValueError('VIEW_CHAT_MAX_CHARS must be a positive integer or unset') + ENABLE_CHANNELS = os.getenv('ENABLE_CHANNELS', 'False').lower() == 'true' ENABLE_ENCRYPTED_CHANNELS = os.getenv('ENABLE_ENCRYPTED_CHANNELS', 'False').lower() == 'true' @@ -2929,6 +2933,7 @@ DEFAULT_CONFIG = { 'skills.lazy_loading.enable': ENABLE_SKILL_LAZY_LOADING, 'skills.content_max_chars': SKILL_CONTENT_MAX_CHARS, 'skills.preview_max_chars': SKILL_PREVIEW_MAX_CHARS, + 'chats.view_max_chars': VIEW_CHAT_MAX_CHARS, 'direct.enable': ENABLE_DIRECT_CONNECTIONS, 'direct.integrations.enable': ENABLE_DIRECT_INTEGRATIONS, 'ollama.enable': ENABLE_OLLAMA_API, diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index cdcbec7a66..b672fe0483 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -1176,8 +1176,6 @@ KNOWLEDGE_GREP_MAX_MATCHES = _int_env('KNOWLEDGE_GREP_MAX_MATCHES', 50) # Characters returned by view_file / view_knowledge_file. VIEW_FILE_MAX_CHARS = _int_env('VIEW_FILE_MAX_CHARS', 100_000) VIEW_FILE_DEFAULT_MAX_CHARS = _int_env('VIEW_FILE_DEFAULT_MAX_CHARS', 10_000) -# Characters returned by view_chat; unset returns the full chat. -VIEW_CHAT_MAX_CHARS = int(os.getenv('VIEW_CHAT_MAX_CHARS')) if os.getenv('VIEW_CHAT_MAX_CHARS') else None # Tools/functions pip options diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 318d2dc786..2a56ba2257 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -140,6 +140,7 @@ ADMIN_CONFIG_KEYS = { 'ENABLE_SKILL_LAZY_LOADING': 'skills.lazy_loading.enable', 'SKILL_CONTENT_MAX_CHARS': 'skills.content_max_chars', 'SKILL_PREVIEW_MAX_CHARS': 'skills.preview_max_chars', + 'VIEW_CHAT_MAX_CHARS': 'chats.view_max_chars', 'ENABLE_NOTES': 'notes.enable', 'ENABLE_USER_WEBHOOKS': 'ui.enable_user_webhooks', 'ENABLE_USER_STATUS': 'users.enable_status', @@ -1182,6 +1183,7 @@ class AdminConfig(MfaConfigForm): ENABLE_SKILL_LAZY_LOADING: bool = False SKILL_CONTENT_MAX_CHARS: int | None = Field(default=None, gt=0, strict=True) SKILL_PREVIEW_MAX_CHARS: int | None = Field(default=None, gt=0, strict=True) + VIEW_CHAT_MAX_CHARS: int | None = Field(default=None, gt=0, strict=True) ENABLE_NOTES: bool ENABLE_USER_WEBHOOKS: bool ENABLE_USER_STATUS: bool @@ -1240,6 +1242,8 @@ async def update_admin_config(request: Request, form_data: AdminConfig, user=Dep updates.pop('skills.content_max_chars', None) if 'SKILL_PREVIEW_MAX_CHARS' not in form_data.model_fields_set: updates.pop('skills.preview_max_chars', None) + if 'VIEW_CHAT_MAX_CHARS' not in form_data.model_fields_set: + updates.pop('chats.view_max_chars', None) updates['ui.default_interface_settings'] = form_data.DEFAULT_INTERFACE_SETTINGS or {} updates['folders.max_file_count'] = int(form_data.FOLDER_MAX_FILE_COUNT) if form_data.FOLDER_MAX_FILE_COUNT else '' updates['automations.max_count'] = int(form_data.AUTOMATION_MAX_COUNT) if form_data.AUTOMATION_MAX_COUNT else '' diff --git a/backend/open_webui/tools/builtin.py b/backend/open_webui/tools/builtin.py index 420d1397d1..9534466fd4 100644 --- a/backend/open_webui/tools/builtin.py +++ b/backend/open_webui/tools/builtin.py @@ -16,7 +16,6 @@ from fastapi import HTTPException, Request from open_webui.env import ( KNOWLEDGE_GREP_MAX_MATCHES, - VIEW_CHAT_MAX_CHARS, VIEW_FILE_DEFAULT_MAX_CHARS, VIEW_FILE_MAX_CHARS, ) @@ -1752,8 +1751,9 @@ async def view_chat( current_id = msg.get('parentId') if msg else None truncation = {} - if VIEW_CHAT_MAX_CHARS and sum(len(message['content']) for message in messages) > VIEW_CHAT_MAX_CHARS: - remaining_chars = VIEW_CHAT_MAX_CHARS + max_chars = await Config.get('chats.view_max_chars') + if max_chars and sum(len(message['content']) for message in messages) > max_chars: + remaining_chars = max_chars kept_messages = [] for message in messages: if remaining_chars <= 0: diff --git a/src/lib/components/admin/Settings/General.svelte b/src/lib/components/admin/Settings/General.svelte index 2ed26d6ebc..12dd20988e 100644 --- a/src/lib/components/admin/Settings/General.svelte +++ b/src/lib/components/admin/Settings/General.svelte @@ -86,6 +86,7 @@ ...adminConfig, SKILL_CONTENT_MAX_CHARS: adminConfig.SKILL_CONTENT_MAX_CHARS ?? null, SKILL_PREVIEW_MAX_CHARS: adminConfig.SKILL_PREVIEW_MAX_CHARS ?? null, + VIEW_CHAT_MAX_CHARS: adminConfig.VIEW_CHAT_MAX_CHARS ?? null, DEFAULT_INTERFACE_SETTINGS: defaultInterfaceSettings, I18N: cleaned }); @@ -378,6 +379,21 @@ bind:value={adminConfig.SKILL_PREVIEW_MAX_CHARS} /> + + + Date: Tue, 3 Nov 2026 19:48:29 +0100 Subject: [PATCH 3/3] fix: chat read limit failing on chats with empty or non-text messages With a chat read limit set, reading an imported chat that held empty or non-text message content ended in an error instead of the chat. Such content is now counted and cut as plain text. --- backend/open_webui/tools/builtin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/tools/builtin.py b/backend/open_webui/tools/builtin.py index 9534466fd4..66b340b255 100644 --- a/backend/open_webui/tools/builtin.py +++ b/backend/open_webui/tools/builtin.py @@ -1752,13 +1752,13 @@ async def view_chat( truncation = {} max_chars = await Config.get('chats.view_max_chars') - if max_chars and sum(len(message['content']) for message in messages) > max_chars: + if max_chars and sum(len(str(message['content'] or '')) for message in messages) > max_chars: remaining_chars = max_chars kept_messages = [] for message in messages: if remaining_chars <= 0: break - content = message['content'][:remaining_chars] + content = str(message['content'] or '')[:remaining_chars] kept_messages.append({**message, 'content': content}) remaining_chars -= len(content) truncation = {