diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py
index 721c890407..b024ce0b7e 100644
--- a/backend/open_webui/config.py
+++ b/backend/open_webui/config.py
@@ -2148,6 +2148,8 @@ TASK_MODEL = os.getenv('TASK_MODEL', '')
TASK_MODEL_EXTERNAL = os.getenv('TASK_MODEL_EXTERNAL', '')
+CONTEXT_COMPACTION_MODEL = os.getenv('CONTEXT_COMPACTION_MODEL', '')
+
ENABLE_CONTEXT_COMPACTION = os.getenv('ENABLE_CONTEXT_COMPACTION', 'False').lower() == 'true'
CONTEXT_COMPACTION_TOKEN_THRESHOLD = int(os.getenv('CONTEXT_COMPACTION_TOKEN_THRESHOLD', '80000'))
@@ -3072,6 +3074,7 @@ DEFAULT_CONFIG = {
'auth.admin.email': ADMIN_EMAIL,
'task.model.default': TASK_MODEL,
'task.model.external': TASK_MODEL_EXTERNAL,
+ 'chat.context_compaction.model': CONTEXT_COMPACTION_MODEL,
'chat.context_compaction.enable': ENABLE_CONTEXT_COMPACTION,
'chat.context_compaction.token_threshold': CONTEXT_COMPACTION_TOKEN_THRESHOLD,
'chat.context_compaction.token_cap': CONTEXT_COMPACTION_TOKEN_CAP,
diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py
index 35dbf70abd..e618eb3f06 100644
--- a/backend/open_webui/routers/chats.py
+++ b/backend/open_webui/routers/chats.py
@@ -53,6 +53,7 @@ router = APIRouter()
SEARCH_FILTER_PREFIXES = ('tag:', 'folder:', 'pinned:', 'archived:', 'shared:')
CHAT_CONFIG_KEYS = {
+ 'CONTEXT_COMPACTION_MODEL': 'chat.context_compaction.model',
'ENABLE_CONTEXT_COMPACTION': 'chat.context_compaction.enable',
'CONTEXT_COMPACTION_TOKEN_THRESHOLD': 'chat.context_compaction.token_threshold',
'CONTEXT_COMPACTION_TOKEN_CAP': 'chat.context_compaction.token_cap',
@@ -134,6 +135,7 @@ async def get_folder_unread_counts(user_id: str, db: AsyncSession | None = None)
class ChatConfigForm(BaseModel):
+ CONTEXT_COMPACTION_MODEL: str | None = ''
ENABLE_CONTEXT_COMPACTION: bool
CONTEXT_COMPACTION_TOKEN_THRESHOLD: int
CONTEXT_COMPACTION_TOKEN_CAP: int | None = None
@@ -185,6 +187,8 @@ def chat_search_snippet(chat: dict, search_text: str, max_length: int = 200) ->
async def get_chat_config_values() -> dict:
values = await Config.get_many(*CHAT_CONFIG_KEYS.values())
config = {field: values[storage_key] for field, storage_key in CHAT_CONFIG_KEYS.items() if storage_key in values}
+ if config.get('CONTEXT_COMPACTION_MODEL') is None:
+ config['CONTEXT_COMPACTION_MODEL'] = ''
if config.get('CONTEXT_COMPACTION_TOKEN_CAP') is None:
config['CONTEXT_COMPACTION_TOKEN_CAP'] = config.get('CONTEXT_COMPACTION_TOKEN_THRESHOLD', 80000)
if config.get('CONTEXT_COMPACTION_RETENTION_PERCENTAGE') is None:
@@ -824,6 +828,7 @@ async def set_chat_config(form_data: ChatConfigForm, user=Depends(get_admin_user
chat_config_updates(
{
**form_data.model_dump(),
+ 'CONTEXT_COMPACTION_MODEL': form_data.CONTEXT_COMPACTION_MODEL or '',
'CONTEXT_COMPACTION_TOKEN_THRESHOLD': threshold,
'CONTEXT_COMPACTION_TOKEN_CAP': token_cap,
'CONTEXT_COMPACTION_RETENTION_PERCENTAGE': retention_percentage,
diff --git a/backend/open_webui/utils/context_compaction.py b/backend/open_webui/utils/context_compaction.py
index 095b920bef..e3c6f54e2a 100644
--- a/backend/open_webui/utils/context_compaction.py
+++ b/backend/open_webui/utils/context_compaction.py
@@ -364,11 +364,21 @@ async def _generate_summary(
) -> str:
from open_webui.utils.chat import generate_chat_completion
- task_model_id = get_task_model_id(
- model_id,
- await Config.get('task.model.default'),
- await Config.get('task.model.external'),
- models,
+ task_config = await Config.get_many(
+ 'task.model.default',
+ 'task.model.external',
+ 'chat.context_compaction.model',
+ )
+ context_compaction_model = task_config.get('chat.context_compaction.model')
+ task_model_id = (
+ context_compaction_model
+ if context_compaction_model in models
+ else get_task_model_id(
+ model_id,
+ task_config.get('task.model.default'),
+ task_config.get('task.model.external'),
+ models,
+ )
)
if task_model_id not in models:
task_model_id = model_id
diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py
index 88738df7ec..9cb936d3ec 100644
--- a/backend/open_webui/utils/middleware.py
+++ b/backend/open_webui/utils/middleware.py
@@ -2332,6 +2332,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
if is_saved_chat_id(chat_id) and user_message_id:
if getattr(request.state, 'direct', False) and hasattr(request.state, 'model'):
compaction_models = {
+ **request.app.state.MODELS,
request.state.model['id']: request.state.model,
}
else:
diff --git a/src/lib/components/admin/Settings/Interface.svelte b/src/lib/components/admin/Settings/Interface.svelte
index c6bb724638..c1cae4b83b 100644
--- a/src/lib/components/admin/Settings/Interface.svelte
+++ b/src/lib/components/admin/Settings/Interface.svelte
@@ -41,6 +41,7 @@
};
let chatConfig = {
+ CONTEXT_COMPACTION_MODEL: '',
ENABLE_CONTEXT_COMPACTION: false,
CONTEXT_COMPACTION_TOKEN_THRESHOLD: 80000,
CONTEXT_COMPACTION_TOKEN_CAP: 80000,
@@ -71,6 +72,27 @@
let models: any[] | null = null;
$: modelOptions = models ?? [];
+ const normalizeModelSelection = (modelId: string | null | undefined) => {
+ if (!modelId) {
+ return '';
+ }
+
+ const model = modelOptions.find((m: any) => m.id === modelId);
+ if (!model) {
+ return '';
+ }
+
+ if (
+ model?.access_grants &&
+ !model.access_grants.some(
+ (g: any) => g.principal_type === 'user' && g.principal_id === '*' && g.permission === 'read'
+ )
+ ) {
+ toast.error($i18n.t('This model is not publicly available. Please select another model.'));
+ }
+
+ return model.id;
+ };
const inputClass =
'w-full h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const textareaClass =
@@ -150,30 +172,7 @@
className="w-full"
placeholder={$i18n.t('Select a model')}
on:change={() => {
- if (taskConfig.TASK_MODEL) {
- const model = modelOptions.find((m: any) => m.id === taskConfig.TASK_MODEL);
- if (model) {
- if (
- model?.access_grants &&
- !model.access_grants.some(
- (g: any) =>
- g.principal_type === 'user' &&
- g.principal_id === '*' &&
- g.permission === 'read'
- )
- ) {
- toast.error(
- $i18n.t(
- 'This model is not publicly available. Please select another model.'
- )
- );
- }
-
- taskConfig.TASK_MODEL = model.id;
- } else {
- taskConfig.TASK_MODEL = '';
- }
- }
+ taskConfig.TASK_MODEL = normalizeModelSelection(taskConfig.TASK_MODEL);
}}
>
@@ -192,32 +191,9 @@
className="w-full"
placeholder={$i18n.t('Select a model')}
on:change={() => {
- if (taskConfig.TASK_MODEL_EXTERNAL) {
- const model = modelOptions.find(
- (m: any) => m.id === taskConfig.TASK_MODEL_EXTERNAL
- );
- if (model) {
- if (
- model?.access_grants &&
- !model.access_grants.some(
- (g: any) =>
- g.principal_type === 'user' &&
- g.principal_id === '*' &&
- g.permission === 'read'
- )
- ) {
- toast.error(
- $i18n.t(
- 'This model is not publicly available. Please select another model.'
- )
- );
- }
-
- taskConfig.TASK_MODEL_EXTERNAL = model.id;
- } else {
- taskConfig.TASK_MODEL_EXTERNAL = '';
- }
- }
+ taskConfig.TASK_MODEL_EXTERNAL = normalizeModelSelection(
+ taskConfig.TASK_MODEL_EXTERNAL
+ );
}}
>
@@ -245,6 +221,32 @@
{#if chatConfig.ENABLE_CONTEXT_COMPACTION}
+
+ {
+ chatConfig.CONTEXT_COMPACTION_MODEL = normalizeModelSelection(
+ chatConfig.CONTEXT_COMPACTION_MODEL
+ );
+ }}
+ >
+
+ {#each modelOptions as model}
+
+ {/each}
+
+
+