feat(memory): complete M.2-M.5 memory upgrade phases with tests
- M.2: ForgettingCurve, MemoryDecay, MemoryReinforcement (selective forgetting) - M.3: DailyDigestGenerator, ReminderScheduler, ProactiveInformer (proactive reminders) - M.4: MemoryExtractor with LLM-based memory extraction from conversations - M.5: MemoryRecallInjector with token budget control for prompt injection - All phases include comprehensive unit tests (109 tests passing) - Updated checklist.md to mark all tasks complete
This commit is contained in:
81
backend/app/services/memory/memory_decay.py
Normal file
81
backend/app/services/memory/memory_decay.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
MemoryDecay
|
||||
|
||||
Handles memory archiving, deprioritization, and restoration.
|
||||
"""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.models.memory import UserMemory
|
||||
|
||||
|
||||
class MemoryDecay:
|
||||
"""Handle memory archiving and deprioritization decisions."""
|
||||
|
||||
ARCHIVE_THRESHOLD = 0.2
|
||||
DEPRIORITIZE_THRESHOLD = 0.5
|
||||
|
||||
def evaluate(self, memory: "UserMemory") -> dict:
|
||||
"""Evaluate memory and return action recommendation.
|
||||
|
||||
Returns:
|
||||
dict with keys: decay_score, should_archive, should_deprioritize, action
|
||||
"""
|
||||
from app.services.memory.forgetting_curve import ForgettingCurve
|
||||
|
||||
curve = ForgettingCurve()
|
||||
decay_score = curve.calculate_decay(memory)
|
||||
archive = decay_score < self.ARCHIVE_THRESHOLD
|
||||
deprioritize = decay_score < self.DEPRIORITIZE_THRESHOLD
|
||||
|
||||
if archive:
|
||||
action = "archive"
|
||||
elif deprioritize:
|
||||
action = "deprioritize"
|
||||
else:
|
||||
action = "keep_active"
|
||||
|
||||
return {
|
||||
"decay_score": decay_score,
|
||||
"should_archive": archive,
|
||||
"should_deprioritize": deprioritize,
|
||||
"action": action,
|
||||
}
|
||||
|
||||
def archive_memory(self, memory: "UserMemory") -> "UserMemory":
|
||||
"""Archive a memory (set is_archived=True, reset decay_score to low value).
|
||||
|
||||
Archived memories are moved to cold storage and not included in
|
||||
active reminders or context injection.
|
||||
"""
|
||||
memory.is_archived = True
|
||||
memory.decay_score = 0.1 # Very low, will be restored on access
|
||||
memory.archive_at = datetime.now(UTC)
|
||||
return memory
|
||||
|
||||
def deprioritize_memory(self, memory: "UserMemory") -> "UserMemory":
|
||||
"""Mark a memory as deprioritized (excluded from active reminders).
|
||||
|
||||
Unlike archival, the memory is still accessible and included in
|
||||
context injection if relevant.
|
||||
"""
|
||||
# Just update decay_score, the importance_level already encodes priority
|
||||
from app.services.memory.forgetting_curve import ForgettingCurve
|
||||
|
||||
curve = ForgettingCurve()
|
||||
memory.decay_score = curve.calculate_decay(memory)
|
||||
return memory
|
||||
|
||||
def restore_from_archive(self, memory: "UserMemory") -> "UserMemory":
|
||||
"""Restore a memory from archive.
|
||||
|
||||
Resets is_archived=False and decay_score=0.8 (strong retention).
|
||||
The memory is moved back to hot storage.
|
||||
"""
|
||||
memory.is_archived = False
|
||||
memory.decay_score = 0.8 # Strong retention after restore
|
||||
memory.last_accessed_at = datetime.now(UTC)
|
||||
memory.archive_at = None
|
||||
return memory
|
||||
Reference in New Issue
Block a user