Simplify chat UI and wire memory context

This commit is contained in:
2026-05-20 23:35:38 +08:00
parent 3d815d3ae3
commit 061cc9225a
8 changed files with 437 additions and 73 deletions
+81 -3
View File
@@ -15,6 +15,7 @@ class MemoryRecord(BaseModel):
id: int | None = None
memory_id: str
text: str
scope: str = "workspace"
workspace: str
conversation_id: str | None = None
memory_type: str = "note"
@@ -37,6 +38,7 @@ class MemoryStore:
id integer primary key autoincrement,
memory_id text not null unique,
text text not null,
scope text not null default 'workspace',
workspace text not null,
conversation_id text,
memory_type text not null,
@@ -53,12 +55,22 @@ class MemoryStore:
on memories(workspace, created_at)
"""
)
await self._ensure_scope_column(db)
await db.commit()
async def _ensure_scope_column(self, db: aiosqlite.Connection) -> None:
cursor = await db.execute("pragma table_info(memories)")
columns = {row[1] for row in await cursor.fetchall()}
if "scope" not in columns:
await db.execute(
"alter table memories add column scope text not null default 'workspace'"
)
async def add(
self,
text: str,
workspace: str,
scope: str = "workspace",
conversation_id: str | None = None,
memory_type: str = "note",
importance: float = 0.5,
@@ -72,13 +84,14 @@ class MemoryStore:
cursor = await db.execute(
"""
insert into memories(
memory_id, text, workspace, conversation_id, memory_type,
memory_id, text, scope, workspace, conversation_id, memory_type,
importance, metadata_json, created_at, updated_at
) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
memory_id,
clean_text,
self._normalize_scope(scope),
workspace,
conversation_id,
memory_type or "note",
@@ -94,6 +107,7 @@ class MemoryStore:
id=row_id,
memory_id=memory_id,
text=clean_text,
scope=self._normalize_scope(scope),
workspace=workspace,
conversation_id=conversation_id,
memory_type=memory_type or "note",
@@ -114,7 +128,7 @@ class MemoryStore:
cursor = await db.execute(
"""
select * from memories
where workspace = ?
where workspace = ? or scope = 'global'
order by importance desc, created_at desc
limit ?
""",
@@ -164,11 +178,75 @@ class MemoryStore:
rows = await cursor.fetchall()
return [self._row_to_record(row) for row in rows]
async def relevant(
self,
workspace: str,
conversation_id: str | None = None,
query: str = "",
limit: int = 8,
) -> list[MemoryRecord]:
await self.init()
bounded_limit = min(max(limit, 1), 30)
terms = [term.lower() for term in query.split() if len(term) >= 3]
async with aiosqlite.connect(self.db_path) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"""
select * from memories
where scope = 'global'
or (scope = 'workspace' and workspace = ?)
or (scope = 'conversation' and workspace = ? and conversation_id = ?)
order by
case scope
when 'global' then 0
when 'conversation' then 1
else 2
end,
importance desc,
created_at desc
limit ?
""",
(workspace, workspace, conversation_id, bounded_limit * 3),
)
rows = await cursor.fetchall()
records = [self._row_to_record(row) for row in rows]
if not terms:
return records[:bounded_limit]
matching = [
record
for record in records
if any(term in record.text.lower() for term in terms)
or record.scope == "global"
]
return matching[:bounded_limit]
def infer_scope(self, text: str, workspace: str, conversation_id: str | None) -> str:
lowered = text.lower()
global_markers = (
"user prefers",
"пользователь предпочитает",
"отвечай",
"always",
"всегда",
"rx580",
"radeon",
"vulkan",
)
if any(marker in lowered for marker in global_markers):
return "global"
if conversation_id and any(marker in lowered for marker in ("this chat", "этот чат", "диалог")):
return "conversation"
return "workspace" if workspace else "global"
def _normalize_scope(self, scope: str) -> str:
return scope if scope in {"global", "workspace", "conversation"} else "workspace"
def _row_to_record(self, row: aiosqlite.Row) -> MemoryRecord:
return MemoryRecord(
id=row["id"],
memory_id=row["memory_id"],
text=row["text"],
scope=row["scope"],
workspace=row["workspace"],
conversation_id=row["conversation_id"],
memory_type=row["memory_type"],