mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-27 19:25:15 +02:00
refactor: remove memory extraction functions and related components from the new chat agent
This commit is contained in:
parent
a0ff86e0e8
commit
132e7b3c44
12 changed files with 2 additions and 375 deletions
|
|
@ -4,7 +4,6 @@ from .schemas import MemoryLimits, MemoryRead
|
|||
from .service import (
|
||||
MemoryScope,
|
||||
SaveResult,
|
||||
extract_and_save,
|
||||
memory_limits,
|
||||
read_memory,
|
||||
reset_memory,
|
||||
|
|
@ -24,7 +23,6 @@ __all__ = [
|
|||
"MemoryRead",
|
||||
"MemoryScope",
|
||||
"SaveResult",
|
||||
"extract_and_save",
|
||||
"memory_limits",
|
||||
"read_memory",
|
||||
"reset_memory",
|
||||
|
|
|
|||
|
|
@ -18,93 +18,3 @@ RULES:
|
|||
<memory_document>
|
||||
{content}
|
||||
</memory_document>"""
|
||||
|
||||
USER_MEMORY_EXTRACT_PROMPT = """\
|
||||
You are a memory extraction assistant. Analyze the user's message and decide \
|
||||
if it contains any long-term information worth persisting to personal memory.
|
||||
|
||||
Worth remembering: preferences, background/identity, goals, projects, \
|
||||
instructions, tools/languages they use, decisions, expertise, workplace — \
|
||||
durable facts that will matter in future conversations.
|
||||
|
||||
NOT worth remembering: greetings, one-off factual questions, session \
|
||||
logistics, ephemeral requests, follow-up clarifications with no new personal \
|
||||
info, things that only matter for the current task.
|
||||
|
||||
If there is nothing durable to remember, choose `action = no_update`.
|
||||
|
||||
If the message contains memorizable information, choose `action = save` and \
|
||||
return the FULL updated memory document with the new information merged into \
|
||||
existing content.
|
||||
|
||||
FORMAT RULES FOR `updated_memory`:
|
||||
- Markdown only.
|
||||
- Every entry should be under a `##` heading.
|
||||
- Recommended headings: `## Facts`, `## Preferences`, `## Instructions`.
|
||||
- New bullets should use: `- YYYY-MM-DD: memory text`.
|
||||
- If current memory uses legacy `(YYYY-MM-DD) [fact|pref|instr]` markers,
|
||||
preserve the information but write the updated document in the new
|
||||
heading-based format.
|
||||
- Use the user's first name from `<user_name>` when helpful, not "the user".
|
||||
- Do not duplicate existing information.
|
||||
|
||||
<user_name>{user_name}</user_name>
|
||||
|
||||
<current_memory>
|
||||
{current_memory}
|
||||
</current_memory>
|
||||
|
||||
<user_message>
|
||||
{user_message}
|
||||
</user_message>"""
|
||||
|
||||
TEAM_MEMORY_EXTRACT_PROMPT = """\
|
||||
You are a team-memory extraction assistant. Analyze the latest message and \
|
||||
decide if it contains durable TEAM-level information worth persisting.
|
||||
|
||||
Decision policy:
|
||||
- Prioritize recall for durable team context, while avoiding personal-only facts.
|
||||
- Do NOT require explicit consensus language. A direct team-level statement can
|
||||
be stored if it is stable and broadly useful for future team chats.
|
||||
- If evidence is weak or clearly tentative, choose `action = no_update`.
|
||||
|
||||
Worth remembering (team-level only):
|
||||
- Decisions and defaults that guide future team work
|
||||
- Team conventions/standards (naming, review policy, coding norms)
|
||||
- Stable org/project facts (locations, ownership, constraints)
|
||||
- Long-lived architecture/process facts
|
||||
- Ongoing priorities that are likely relevant beyond this turn
|
||||
|
||||
NOT worth remembering:
|
||||
- Personal preferences or biography of one person
|
||||
- Questions, brainstorming, tentative ideas, or speculation
|
||||
- One-off requests, status updates, TODOs, logistics for this session
|
||||
- Information scoped only to a single ephemeral task
|
||||
|
||||
If the message contains memorizable team information, choose `action = save` \
|
||||
and return the FULL updated team memory document with new facts merged into \
|
||||
existing content.
|
||||
|
||||
FORMAT RULES FOR `updated_memory`:
|
||||
- Markdown only.
|
||||
- Every entry should be under a `##` heading.
|
||||
- Recommended headings: `## Product Decisions`, `## Engineering Conventions`,
|
||||
`## Project Facts`, `## Open Questions`.
|
||||
- New bullets should use: `- YYYY-MM-DD: memory text`.
|
||||
- If current memory uses legacy `(YYYY-MM-DD) [fact]` markers, preserve the
|
||||
information but write the updated document in the new heading-based format.
|
||||
- Do not create personal headings such as `## Preferences`, `## Instructions`,
|
||||
or `## Personal Notes`.
|
||||
- Preserve neutral team phrasing; avoid person-specific memory unless role-anchored.
|
||||
|
||||
<current_team_memory>
|
||||
{current_memory}
|
||||
</current_team_memory>
|
||||
|
||||
<latest_message_author>
|
||||
{author}
|
||||
</latest_message_author>
|
||||
|
||||
<latest_message>
|
||||
{user_message}
|
||||
</latest_message>"""
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class MemoryLimits(BaseModel):
|
||||
|
|
@ -19,19 +17,3 @@ class MemoryRead(BaseModel):
|
|||
|
||||
memory_md: str
|
||||
limits: MemoryLimits
|
||||
|
||||
|
||||
class MemoryExtractionDecision(BaseModel):
|
||||
"""Structured extraction result; avoids string sentinel parsing."""
|
||||
|
||||
action: Literal["no_update", "save"] = Field(
|
||||
description="Choose no_update when nothing durable should be saved; choose save otherwise."
|
||||
)
|
||||
reason: str | None = Field(
|
||||
default=None,
|
||||
description="Short reason for no_update, or brief summary of the memory update.",
|
||||
)
|
||||
updated_memory: str | None = Field(
|
||||
default=None,
|
||||
description="The full updated markdown memory document when action is save.",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,18 +8,13 @@ from enum import StrEnum
|
|||
from typing import Any, Literal
|
||||
from uuid import UUID
|
||||
|
||||
from langchain_core.messages import HumanMessage
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import SearchSpace, User
|
||||
from app.services.memory.document import parse_memory_document, render_memory_document
|
||||
from app.services.memory.prompts import (
|
||||
TEAM_MEMORY_EXTRACT_PROMPT,
|
||||
USER_MEMORY_EXTRACT_PROMPT,
|
||||
)
|
||||
from app.services.memory.rewrite import forced_rewrite
|
||||
from app.services.memory.schemas import MemoryExtractionDecision, MemoryLimits
|
||||
from app.services.memory.schemas import MemoryLimits
|
||||
from app.services.memory.validation import (
|
||||
MEMORY_HARD_LIMIT,
|
||||
MEMORY_SOFT_LIMIT,
|
||||
|
|
@ -234,74 +229,3 @@ async def reset_memory(
|
|||
session=session,
|
||||
llm=None,
|
||||
)
|
||||
|
||||
|
||||
async def extract_and_save(
|
||||
*,
|
||||
scope: MemoryScope | str,
|
||||
target_id: str | int | UUID,
|
||||
user_message: str,
|
||||
actor_display_name: str | None,
|
||||
session: AsyncSession,
|
||||
llm: Any,
|
||||
) -> SaveResult:
|
||||
normalized = _normalize_scope(scope)
|
||||
current_memory = await read_memory(
|
||||
scope=normalized,
|
||||
target_id=target_id,
|
||||
session=session,
|
||||
)
|
||||
|
||||
if normalized is MemoryScope.USER:
|
||||
first_name = (
|
||||
actor_display_name.strip().split()[0]
|
||||
if actor_display_name and actor_display_name.strip()
|
||||
else "The user"
|
||||
)
|
||||
prompt = USER_MEMORY_EXTRACT_PROMPT.format(
|
||||
current_memory=current_memory or "(empty)",
|
||||
user_message=user_message,
|
||||
user_name=first_name,
|
||||
)
|
||||
else:
|
||||
prompt = TEAM_MEMORY_EXTRACT_PROMPT.format(
|
||||
current_memory=current_memory or "(empty)",
|
||||
author=actor_display_name or "Unknown team member",
|
||||
user_message=user_message,
|
||||
)
|
||||
|
||||
try:
|
||||
structured = llm.with_structured_output(MemoryExtractionDecision)
|
||||
decision = await structured.ainvoke(
|
||||
[HumanMessage(content=prompt)],
|
||||
config={"tags": ["surfsense:internal", "memory-extraction"]},
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("Structured memory extraction failed")
|
||||
return SaveResult(
|
||||
status="error",
|
||||
message="Structured memory extraction failed.",
|
||||
memory_md=current_memory,
|
||||
)
|
||||
|
||||
if decision.action == "no_update":
|
||||
return SaveResult(
|
||||
status="no_op",
|
||||
message=decision.reason or "No durable memory to persist.",
|
||||
memory_md=current_memory,
|
||||
)
|
||||
|
||||
if not decision.updated_memory:
|
||||
return SaveResult(
|
||||
status="error",
|
||||
message="Structured memory extraction chose save without updated_memory.",
|
||||
memory_md=current_memory,
|
||||
)
|
||||
|
||||
return await save_memory(
|
||||
scope=normalized,
|
||||
target_id=target_id,
|
||||
content=decision.updated_memory,
|
||||
session=session,
|
||||
llm=llm,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue