diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index f89738b0db..19da0f561e 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -2128,6 +2128,10 @@ ENABLE_CONTEXT_COMPACTION = os.getenv('ENABLE_CONTEXT_COMPACTION', 'False').lowe CONTEXT_COMPACTION_TOKEN_THRESHOLD = int(os.getenv('CONTEXT_COMPACTION_TOKEN_THRESHOLD', '80000')) +CONTEXT_COMPACTION_TOKEN_CAP = int( + os.getenv('CONTEXT_COMPACTION_TOKEN_CAP', os.getenv('CONTEXT_COMPACTION_TOKEN_THRESHOLD', '80000')) +) + CONTEXT_COMPACTION_PROMPT_TEMPLATE = os.getenv('CONTEXT_COMPACTION_PROMPT_TEMPLATE', '') TITLE_GENERATION_PROMPT_TEMPLATE = os.getenv('TITLE_GENERATION_PROMPT_TEMPLATE', '') @@ -3034,6 +3038,7 @@ DEFAULT_CONFIG = { 'task.model.external': TASK_MODEL_EXTERNAL, '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, 'chat.context_compaction.prompt_template': CONTEXT_COMPACTION_PROMPT_TEMPLATE, 'task.title.prompt_template': TITLE_GENERATION_PROMPT_TEMPLATE, 'task.tags.prompt_template': TAGS_GENERATION_PROMPT_TEMPLATE, diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py index 98621ff80e..d9f6e6706b 100644 --- a/backend/open_webui/routers/chats.py +++ b/backend/open_webui/routers/chats.py @@ -51,6 +51,7 @@ SEARCH_FILTER_PREFIXES = ('tag:', 'folder:', 'pinned:', 'archived:', 'shared:') CHAT_CONFIG_KEYS = { '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', 'CONTEXT_COMPACTION_PROMPT_TEMPLATE': 'chat.context_compaction.prompt_template', } @@ -58,6 +59,7 @@ CHAT_CONFIG_KEYS = { class ChatConfigForm(BaseModel): ENABLE_CONTEXT_COMPACTION: bool CONTEXT_COMPACTION_TOKEN_THRESHOLD: int + CONTEXT_COMPACTION_TOKEN_CAP: int CONTEXT_COMPACTION_PROMPT_TEMPLATE: str @@ -712,11 +714,13 @@ async def get_chat_config(user=Depends(get_admin_user)): @router.post('/config', response_model=ChatConfigForm) async def set_chat_config(form_data: ChatConfigForm, user=Depends(get_admin_user)): threshold = max(1, int(form_data.CONTEXT_COMPACTION_TOKEN_THRESHOLD)) + token_cap = max(1, int(form_data.CONTEXT_COMPACTION_TOKEN_CAP)) await Config.upsert( chat_config_updates( { **form_data.model_dump(), 'CONTEXT_COMPACTION_TOKEN_THRESHOLD': threshold, + 'CONTEXT_COMPACTION_TOKEN_CAP': token_cap, } ) ) diff --git a/backend/open_webui/utils/context_compaction.py b/backend/open_webui/utils/context_compaction.py index 209f56892f..2cdc4c3aef 100644 --- a/backend/open_webui/utils/context_compaction.py +++ b/backend/open_webui/utils/context_compaction.py @@ -53,7 +53,7 @@ async def compact_messages_for_request( return messages, None, False messages, previous_summary = _apply_latest_summary_checkpoint(messages) - token_threshold = _resolve_token_threshold(config['token_threshold'], metadata) + token_threshold = _resolve_token_threshold(config['token_threshold'], config['token_cap'], metadata) if not _exceeds_token_threshold(messages, system_prompt, previous_summary, token_threshold) or len(messages) <= 3: return messages, previous_summary, False @@ -186,11 +186,14 @@ async def _load_config() -> dict: values = await Config.get_many( 'chat.context_compaction.enable', 'chat.context_compaction.token_threshold', + 'chat.context_compaction.token_cap', 'chat.context_compaction.prompt_template', ) + token_threshold = _parse_positive_int(values.get('chat.context_compaction.token_threshold')) or 80000 return { 'enable': bool(values.get('chat.context_compaction.enable', False)), - 'token_threshold': int(values.get('chat.context_compaction.token_threshold', 80000) or 80000), + 'token_threshold': token_threshold, + 'token_cap': _parse_positive_int(values.get('chat.context_compaction.token_cap')) or token_threshold, 'prompt_template': values.get('chat.context_compaction.prompt_template', '') or '', } @@ -203,9 +206,9 @@ def _parse_positive_int(value: Any) -> int | None: return parsed if parsed > 0 else None -def _resolve_token_threshold(global_threshold: int, metadata: dict) -> int: +def _resolve_token_threshold(global_threshold: int, global_cap: int, metadata: dict) -> int: configured_threshold = _parse_positive_int((metadata.get('params') or {}).get('compact_token_threshold')) - return configured_threshold or global_threshold + return min(configured_threshold or global_threshold, global_cap) def _apply_latest_summary_checkpoint(messages: list[dict]) -> tuple[list[dict], str | None]: