diff --git a/backend/open_webui/migrations/versions/7b3f2a9c1d4e_add_memory_kind.py b/backend/open_webui/migrations/versions/7b3f2a9c1d4e_add_memory_kind.py new file mode 100644 index 0000000000..471aac9657 --- /dev/null +++ b/backend/open_webui/migrations/versions/7b3f2a9c1d4e_add_memory_kind.py @@ -0,0 +1,44 @@ +"""add memory type + +Revision ID: 7b3f2a9c1d4e +Revises: 4c5ce3d2f27f +Create Date: 2026-06-25 00:00:00.000000 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +revision: str = '7b3f2a9c1d4e' +down_revision: Union[str, None] = '4c5ce3d2f27f' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = {column['name'] for column in inspector.get_columns('memory')} + indexes = {index['name'] for index in inspector.get_indexes('memory')} + + if 'type' not in columns: + op.add_column('memory', sa.Column('type', sa.String(), server_default='context', nullable=False)) + + if 'ix_memory_kind' not in indexes: + op.create_index('ix_memory_kind', 'memory', ['type']) + + +def downgrade() -> None: + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = {column['name'] for column in inspector.get_columns('memory')} + indexes = {index['name'] for index in inspector.get_indexes('memory')} + + if 'ix_memory_kind' in indexes: + op.drop_index('ix_memory_kind', table_name='memory') + + if 'type' in columns: + op.drop_column('memory', 'type') diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 4c17d720a5..50cd42d131 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -110,7 +110,7 @@ from open_webui.utils.misc import ( set_last_user_message_content, strip_empty_content_blocks, ) -from open_webui.utils.payload import apply_system_prompt_to_body +from open_webui.utils.payload import apply_system_prompt_to_body, resolve_system_prompt from open_webui.utils.plugin import load_function_module_by_id from open_webui.utils.response import merge_usage, normalize_usage from open_webui.utils.sanitize import sanitize_code @@ -2650,8 +2650,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): if 'memory' in features and features['memory']: # Skip forced memory injection when native FC is enabled - model can use memory tools - if metadata.get('params', {}).get('function_calling') == 'legacy': - form_data = await chat_memory_handler(request, form_data, extra_params, user) + form_data = await chat_memory_handler(request, form_data, user) if 'web_search' in features and features['web_search']: # Skip forced RAG web search when native FC is enabled - model can use web_search tool @@ -3359,6 +3358,17 @@ async def background_tasks_handler(ctx): except Exception as e: pass + if messages: + await review_memory_after_turn( + request=request, + user=user, + model=ctx['model'], + metadata=metadata, + form_data=form_data, + assistant_message=ctx.get('assistant_message') or {}, + messages=messages, + ) + async def outlet_filter_handler(ctx): """Run outlet filters inline after chat completion. diff --git a/backend/open_webui/utils/payload.py b/backend/open_webui/utils/payload.py index 7799fa4754..d6242261db 100644 --- a/backend/open_webui/utils/payload.py +++ b/backend/open_webui/utils/payload.py @@ -10,6 +10,26 @@ from open_webui.utils.misc import ( from open_webui.utils.task import prompt_template, prompt_variables_template +async def resolve_system_prompt( + system: Optional[str], + metadata: Optional[dict] = None, + user=None, +) -> str: + if not system: + return '' + + # Metadata (WebUI Usage) + if metadata: + variables = metadata.get('variables', {}) + if variables: + system = prompt_variables_template(system, variables) + + # Legacy (API Usage) + system = await prompt_template(system, user) + + return system + + # What goes out cannot be taken back. Let it be shaped # well before it leaves this place. # inplace function: form_data is modified @@ -20,18 +40,10 @@ async def apply_system_prompt_to_body( user=None, replace: bool = False, ) -> dict: + system = await resolve_system_prompt(system, metadata, user) if not system: return form_data - # Metadata (WebUI Usage) - if metadata: - variables = metadata.get('variables', {}) - if variables: - system = prompt_variables_template(system, variables) - - # Legacy (API Usage) - system = await prompt_template(system, user) - if replace: form_data['messages'] = replace_system_message_content(system, form_data.get('messages', [])) else: