refac
This commit is contained in:
@@ -56,7 +56,7 @@ async def compact_messages_for_request(
|
||||
if not _exceeds_token_threshold(messages, system_prompt, previous_summary, token_threshold) or len(messages) <= 3:
|
||||
return messages, previous_summary, False
|
||||
|
||||
boundary = _find_compaction_boundary(messages)
|
||||
boundary = _find_compaction_boundary(messages, config['retention_percentage'])
|
||||
compacted_messages = messages[:boundary]
|
||||
recent_messages = messages[boundary:]
|
||||
if not compacted_messages or not recent_messages:
|
||||
@@ -196,6 +196,7 @@ async def _load_config() -> dict:
|
||||
'chat.context_compaction.enable',
|
||||
'chat.context_compaction.token_threshold',
|
||||
'chat.context_compaction.token_cap',
|
||||
'chat.context_compaction.retention_percentage',
|
||||
'chat.context_compaction.prompt_template',
|
||||
)
|
||||
token_threshold = _parse_positive_int(values.get('chat.context_compaction.token_threshold')) or 80000
|
||||
@@ -203,6 +204,9 @@ async def _load_config() -> dict:
|
||||
'enable': bool(values.get('chat.context_compaction.enable', False)),
|
||||
'token_threshold': token_threshold,
|
||||
'token_cap': _parse_positive_int(values.get('chat.context_compaction.token_cap')) or token_threshold,
|
||||
'retention_percentage': _clamp_retention_percentage(
|
||||
values.get('chat.context_compaction.retention_percentage')
|
||||
),
|
||||
'prompt_template': values.get('chat.context_compaction.prompt_template', '') or '',
|
||||
}
|
||||
|
||||
@@ -215,6 +219,14 @@ def _parse_positive_int(value: Any) -> int | None:
|
||||
return parsed if parsed > 0 else None
|
||||
|
||||
|
||||
def _clamp_retention_percentage(value: Any) -> int:
|
||||
try:
|
||||
parsed = int(value)
|
||||
except (TypeError, ValueError):
|
||||
parsed = 40
|
||||
return min(50, max(10, parsed))
|
||||
|
||||
|
||||
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 min(configured_threshold or global_threshold, global_cap)
|
||||
@@ -297,8 +309,9 @@ def _exceeds_token_threshold(messages: list[dict], system_prompt: str, summary:
|
||||
return estimated > threshold
|
||||
|
||||
|
||||
def _find_compaction_boundary(messages: list[dict]) -> int:
|
||||
keep_count = max(2, len(messages) * 2 // 5)
|
||||
def _find_compaction_boundary(messages: list[dict], retention_percentage: int = 40) -> int:
|
||||
retention_percentage = _clamp_retention_percentage(retention_percentage)
|
||||
keep_count = max(2, len(messages) * retention_percentage // 100)
|
||||
target = max(1, len(messages) - keep_count)
|
||||
boundaries = [idx for idx, message in enumerate(messages) if message.get('role') == 'user'][1:]
|
||||
return next((idx for idx in reversed(boundaries) if idx <= target), 0)
|
||||
|
||||
@@ -107,6 +107,7 @@ from open_webui.utils.misc import (
|
||||
get_last_user_message,
|
||||
get_last_user_message_item,
|
||||
get_message_list,
|
||||
get_output_text,
|
||||
get_system_message,
|
||||
is_string_allowed,
|
||||
merge_system_messages,
|
||||
@@ -137,17 +138,6 @@ logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _last_output_text(output: list | None) -> str:
|
||||
for item in reversed(output or []):
|
||||
if item.get('type') != 'message':
|
||||
continue
|
||||
parts = item.get('content') or []
|
||||
text = ''.join(str(part.get('text') or '') for part in parts if part.get('type') == 'output_text')
|
||||
if text:
|
||||
return text
|
||||
return ''
|
||||
|
||||
|
||||
async def publish_chat_finished_event(
|
||||
request: Request, user: UserModel, metadata: dict, title: str, content: str, output: list | None = None
|
||||
):
|
||||
@@ -155,7 +145,7 @@ async def publish_chat_finished_event(
|
||||
if getattr(request.state, 'internal', False) is True or not chat_id or chat_id.startswith(('channel:', 'local:')):
|
||||
return
|
||||
|
||||
content = content or _last_output_text(output)
|
||||
content = content or get_output_text(output)
|
||||
webui_url = await Config.get('webui.url')
|
||||
await publish_event(
|
||||
request,
|
||||
@@ -3398,7 +3388,7 @@ async def outlet_filter_handler(ctx):
|
||||
message_list = [
|
||||
{
|
||||
'role': m.get('role'),
|
||||
'content': m.get('content', ''),
|
||||
'content': m.get('content') or get_output_text(m.get('output')),
|
||||
}
|
||||
for m in form_messages
|
||||
]
|
||||
@@ -3431,7 +3421,7 @@ async def outlet_filter_handler(ctx):
|
||||
{
|
||||
'id': m.get('id'),
|
||||
'role': m.get('role'),
|
||||
'content': m.get('content', ''),
|
||||
'content': m.get('content') or get_output_text(m.get('output')),
|
||||
'info': m.get('info'),
|
||||
'timestamp': m.get('timestamp'),
|
||||
**({'output': m['output']} if m.get('output') else {}),
|
||||
@@ -3483,17 +3473,21 @@ async def outlet_filter_handler(ctx):
|
||||
outlet_message_id = message.get('id')
|
||||
if outlet_message_id and outlet_message_id in messages_map:
|
||||
original_message = messages_map[outlet_message_id]
|
||||
content_changed = original_message.get('content') != message.get('content')
|
||||
original_content = original_message.get('content') or get_output_text(
|
||||
original_message.get('output')
|
||||
)
|
||||
message_content = message.get('content') or get_output_text(message.get('output'))
|
||||
content_changed = original_content != message_content
|
||||
output_changed = message.get('output') and message.get('output') != original_message.get(
|
||||
'output'
|
||||
)
|
||||
if content_changed or output_changed:
|
||||
message_update = {
|
||||
'originalContent': original_message.get('content'),
|
||||
'originalContent': original_content,
|
||||
**({'output': message['output']} if output_changed else {}),
|
||||
}
|
||||
if content_changed:
|
||||
message_update['content'] = message.get('content', '')
|
||||
message_update['content'] = message_content or ''
|
||||
await Chats.upsert_message_to_chat_by_id_and_message_id(
|
||||
chat_id,
|
||||
outlet_message_id,
|
||||
|
||||
@@ -160,13 +160,40 @@ def get_last_user_message_item(messages: list[dict]) -> dict | None:
|
||||
|
||||
|
||||
def get_content_from_message(message: dict) -> str | None:
|
||||
if isinstance(message.get('content'), list):
|
||||
for item in message['content']:
|
||||
if item['type'] == 'text':
|
||||
return item['text']
|
||||
else:
|
||||
return message.get('content')
|
||||
return None
|
||||
content = message.get('content')
|
||||
if isinstance(content, list):
|
||||
for item in content:
|
||||
if isinstance(item, dict) and item.get('type') == 'text':
|
||||
return item.get('text')
|
||||
elif content:
|
||||
return content
|
||||
|
||||
output_text = get_output_text(message.get('output'))
|
||||
return output_text or (content if isinstance(content, str) else None)
|
||||
|
||||
|
||||
def get_output_text(output: list | None) -> str:
|
||||
if not isinstance(output, list):
|
||||
return ''
|
||||
|
||||
texts = []
|
||||
for item in output:
|
||||
if not isinstance(item, dict) or item.get('type') != 'message':
|
||||
continue
|
||||
|
||||
parts = item.get('content') or []
|
||||
if not isinstance(parts, list):
|
||||
continue
|
||||
|
||||
text = ''.join(
|
||||
str(part.get('text'))
|
||||
for part in parts
|
||||
if isinstance(part, dict) and part.get('text') is not None
|
||||
)
|
||||
if text.strip():
|
||||
texts.append(text)
|
||||
|
||||
return '\n'.join(texts)
|
||||
|
||||
|
||||
def reconcile_tool_pairs(messages: list[dict]) -> list[dict]:
|
||||
@@ -213,7 +240,7 @@ def reconcile_tool_pairs(messages: list[dict]) -> list[dict]:
|
||||
|
||||
# All tool_calls were orphans — keep the message only if it
|
||||
# carries meaningful text or reasoning content.
|
||||
content = message.get('content', '')
|
||||
content = get_content_from_message(message) or ''
|
||||
has_meaningful_content = content.strip() if isinstance(content, str) else content
|
||||
if has_meaningful_content or message.get('reasoning_content'):
|
||||
reconciled_messages.append({key: value for key, value in message.items() if key != 'tool_calls'})
|
||||
|
||||
Reference in New Issue
Block a user