This commit is contained in:
Timothy Jaeryang Baek committed 2026-06-29 05:49:54 -05:00
1 parent 70e4ffcc65
commit b0487dd6dd
1 file changed
+38 -3
+38 -3
View File
@@ -4,11 +4,11 @@ from __future__ import annotations
import time import time
import uuid import uuid
from typing import Literal, Optional from typing import Literal
from open_webui.internal.db import Base, get_async_db_context from open_webui.internal.db import Base, get_async_db_context
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, String, Text, delete, select from sqlalchemy import JSON, BigInteger, Column, String, Text, delete, select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@@ -20,7 +20,9 @@ class Memory(Base): # user memory store
id = Column(String, primary_key=True, unique=True) id = Column(String, primary_key=True, unique=True)
user_id = Column(String, index=True) user_id = Column(String, index=True)
type = Column(String, default='context', server_default='context', index=True) type = Column(String, default='context', server_default='context', index=True)
path = Column(Text, nullable=True)
content = Column(Text) # free-form text learned from conversation content = Column(Text) # free-form text learned from conversation
meta = Column(JSON, nullable=True)
updated_at = Column(BigInteger) # epoch seconds updated_at = Column(BigInteger) # epoch seconds
created_at = Column(BigInteger) # epoch seconds created_at = Column(BigInteger) # epoch seconds
@@ -31,7 +33,9 @@ class MemoryModel(BaseModel):
id: str id: str
user_id: str user_id: str
type: Literal['user', 'context'] = 'context' type: Literal['user', 'context'] = 'context'
path: str | None = None
content: str content: str
meta: dict | None = None
updated_at: int # timestamp in epoch updated_at: int # timestamp in epoch
created_at: int # timestamp in epoch created_at: int # timestamp in epoch
model_config = ConfigDict(from_attributes=True) # allows ORM mapping model_config = ConfigDict(from_attributes=True) # allows ORM mapping
@@ -47,6 +51,8 @@ class MemoriesTable:
user_id: str, user_id: str,
content: str, content: str,
memory_type: str | None = None, memory_type: str | None = None,
path: str | None = None,
meta: dict | None = None,
db: AsyncSession | None = None, db: AsyncSession | None = None,
) -> MemoryModel | None: ) -> MemoryModel | None:
"""Persist a new memory entry and return the created model.""" """Persist a new memory entry and return the created model."""
@@ -56,7 +62,9 @@ class MemoriesTable:
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
user_id=user_id, user_id=user_id,
type=self.normalize_memory_type(memory_type), type=self.normalize_memory_type(memory_type),
path=path,
content=content, content=content,
meta=meta,
created_at=now, created_at=now,
updated_at=now, updated_at=now,
) )
@@ -71,6 +79,9 @@ class MemoriesTable:
user_id: str, user_id: str,
content: str | None, content: str | None,
memory_type: str | None = None, memory_type: str | None = None,
path: str | None = None,
update_path: bool = False,
meta: dict | None = None,
db: AsyncSession | None = None, db: AsyncSession | None = None,
) -> MemoryModel | None: ) -> MemoryModel | None:
async with get_async_db_context(db) as db: async with get_async_db_context(db) as db:
@@ -83,6 +94,10 @@ class MemoriesTable:
memory.content = content memory.content = content
if memory_type is not None: if memory_type is not None:
memory.type = self.normalize_memory_type(memory_type) memory.type = self.normalize_memory_type(memory_type)
if update_path:
memory.path = path
if meta is not None:
memory.meta = {**(memory.meta or {}), **meta}
memory.updated_at = int(time.time()) memory.updated_at = int(time.time())
await db.commit() await db.commit()
@@ -167,8 +182,9 @@ class MemoriesTable:
if action == 'add': if action == 'add':
content = operation.get('content', '').strip() content = operation.get('content', '').strip()
memory_type = self.normalize_memory_type(operation.get('type')) memory_type = self.normalize_memory_type(operation.get('type'))
path = operation.get('path')
result = await db.execute( result = await db.execute(
select(Memory).filter_by(user_id=user_id, content=content, type=memory_type) select(Memory).filter_by(user_id=user_id, content=content, type=memory_type, path=path)
) )
existing = result.scalars().first() existing = result.scalars().first()
if existing: if existing:
@@ -186,7 +202,9 @@ class MemoriesTable:
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
user_id=user_id, user_id=user_id,
type=memory_type, type=memory_type,
path=path,
content=content, content=content,
meta=operation.get('meta'),
created_at=now, created_at=now,
updated_at=now, updated_at=now,
) )
@@ -204,6 +222,23 @@ class MemoriesTable:
memory.content = content memory.content = content
if operation.get('type') is not None: if operation.get('type') is not None:
memory.type = self.normalize_memory_type(operation.get('type')) memory.type = self.normalize_memory_type(operation.get('type'))
if 'path' in operation:
memory.path = operation.get('path')
if operation.get('meta') is not None:
memory.meta = {**(memory.meta or {}), **operation.get('meta')}
memory.updated_at = now
await db.flush()
results.append({'action': action, 'status': 'updated', 'memory': MemoryModel.model_validate(memory)})
elif action == 'move':
memory_id = operation.get('id')
memory = await db.get(Memory, memory_id)
if not memory or memory.user_id != user_id:
raise ValueError(f'Memory not found: {memory_id}')
memory.path = operation.get('path')
if operation.get('meta') is not None:
memory.meta = {**(memory.meta or {}), **operation.get('meta')}
memory.updated_at = now memory.updated_at = now
await db.flush() await db.flush()
results.append({'action': action, 'status': 'updated', 'memory': MemoryModel.model_validate(memory)}) results.append({'action': action, 'status': 'updated', 'memory': MemoryModel.model_validate(memory)})