hoe / Super-main-3 /src /memory /associative.py
Cunt1257's picture
Upload 136 files
f02fdcc verified
Raw
History Blame Contribute Delete
3.32 kB
"""
Associative Memory (A-Mem) β€” graph-based, Zettelkasten-inspired.
Each node is a MemoryItem enriched with keywords, metadata tags, and
contextual relationships. New nodes retroactively rewire existing links
based on semantic overlap, mimicking the A-Mem self-wiring mechanism.
"""
from datetime import datetime, timezone
from typing import Dict, List, Optional, Set, Tuple
from .models import MemoryItem, MemoryTier, RetrievalResult
from .retriever import retrieve
class AssociativeMemory:
def __init__(self):
self._nodes: Dict[str, MemoryItem] = {}
# adjacency: item_id β†’ set of (related_id, relation_label)
self._edges: Dict[str, List[Tuple[str, str]]] = {}
# ── Node operations ──────────────────────────────────────────────
def add(self, item: MemoryItem) -> MemoryItem:
item.tier = MemoryTier.ASSOCIATIVE
self._nodes[item.item_id] = item
self._edges.setdefault(item.item_id, [])
self._auto_wire(item)
return item
def get(self, item_id: str) -> Optional[MemoryItem]:
return self._nodes.get(item_id)
def all(self) -> List[MemoryItem]:
return list(self._nodes.values())
def remove(self, item_id: str) -> None:
self._nodes.pop(item_id, None)
self._edges.pop(item_id, None)
for edges in self._edges.values():
edges[:] = [(rid, rel) for rid, rel in edges if rid != item_id]
# ── Link operations ───────────────────────────────────────────────
def link(self, source_id: str, target_id: str, relation: str = "related") -> None:
if source_id in self._nodes and target_id in self._nodes:
self._edges[source_id].append((target_id, relation))
def neighbors(self, item_id: str) -> List[Tuple[MemoryItem, str]]:
return [
(self._nodes[rid], rel)
for rid, rel in self._edges.get(item_id, [])
if rid in self._nodes
]
# ── Retrieval ─────────────────────────────────────────────────────
def retrieve(self, query: str, top_k: int = 5) -> List[RetrievalResult]:
results = retrieve(query, self.all(), top_k=top_k)
for r in results:
r.item.usage_count += 1
r.item.updated_at = datetime.now(timezone.utc)
return results
# ── Auto-wiring (retroactive Zettelkasten rewiring) ───────────────
def _auto_wire(self, new_item: MemoryItem, threshold: float = 0.2) -> None:
query = new_item.content + " " + " ".join(new_item.tags)
candidates = [n for n in self._nodes.values() if n.item_id != new_item.item_id]
if not candidates:
return
from .retriever import score_item
max_u = max((c.usage_count for c in candidates), default=1) or 1
for node in candidates:
result = score_item(query, node, max_u)
if result.similarity >= threshold:
self.link(new_item.item_id, node.item_id, "semantic_link")