fix: improve auto-edge reliability — error visibility, similarity-based strength, heuristic docs

- Surface auto-edge errors in store response (auto_edge_error key) instead
  of silently swallowing exceptions, making failures diagnosable
- Pass similarity score as edge strength so higher-confidence matches get
  stronger edges instead of always defaulting to 0.8
- Document why same-type pairs and REQUIRES/FOLLOWS are excluded from the
  heuristic table vs edge-proposer.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-02-28 22:11:15 -06:00
parent 194990d424
commit 5312402a7b

View File

@ -22,6 +22,9 @@ SYNC_SCRIPT = Path(__file__).parent / "scripts" / "memory-git-sync.sh"
# Auto-edge heuristics: (new_type, match_type) -> (rel_type, direction) # Auto-edge heuristics: (new_type, match_type) -> (rel_type, direction)
# direction "ab" = new->match, "ba" = match->new # direction "ab" = new->match, "ba" = match->new
# Subset of edge-proposer.py's TYPE_HEURISTICS — excludes:
# - Same-type pairs (decision/decision, fix/fix, solution/solution): too noisy for auto-edges
# - REQUIRES/FOLLOWS pairs: need stronger signal than title recall provides
AUTO_EDGE_HEURISTICS = { AUTO_EDGE_HEURISTICS = {
("fix", "problem"): ("SOLVES", "ab"), ("fix", "problem"): ("SOLVES", "ab"),
("solution", "problem"): ("SOLVES", "ab"), ("solution", "problem"): ("SOLVES", "ab"),
@ -555,7 +558,8 @@ def _auto_create_edges(
Uses recall to find similar memories and heuristic type-pairs to choose Uses recall to find similar memories and heuristic type-pairs to choose
relationship types. Returns list of created edge info dicts. relationship types. Returns list of created edge info dicts.
Never raises failures are silently swallowed so store always succeeds. Never raises returns partial results or empty list with error info so
store always succeeds. Returns (edges_list, error_string_or_None).
""" """
try: try:
# Build recall query from title + tags for better precision (#4) # Build recall query from title + tags for better precision (#4)
@ -613,11 +617,15 @@ def _auto_create_edges(
from_title = title if from_id == memory_id else match_title from_title = title if from_id == memory_id else match_title
to_title = match_title if to_id == match_id else title to_title = match_title if to_id == match_id else title
desc = f"Auto-edge: {from_title}{to_title}" desc = f"Auto-edge: {from_title}{to_title}"
# Use similarity as edge strength when available (default 0.8)
sim = result.get("similarity")
strength = round(sim, 2) if sim is not None else 0.8
edge_id = client.relate( edge_id = client.relate(
from_id=from_id, from_id=from_id,
to_id=to_id, to_id=to_id,
rel_type=rel_type, rel_type=rel_type,
description=desc, description=desc,
strength=strength,
) )
if edge_id: # Empty string means duplicate if edge_id: # Empty string means duplicate
@ -630,9 +638,9 @@ def _auto_create_edges(
} }
) )
return created_edges return created_edges, None
except Exception: except Exception as e:
return [] return [], f"{type(e).__name__}: {e}"
def handle_tool_call(tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]: def handle_tool_call(tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
@ -669,16 +677,19 @@ def handle_tool_call(tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any
tags=tags, tags=tags,
) )
episode_logged = True episode_logged = True
auto_edges = _auto_create_edges(client, memory_id, title, mem_type, tags) auto_edges, auto_edge_error = _auto_create_edges(
_trigger_git_sync() client, memory_id, title, mem_type, tags
return ok(
{
"success": True,
"memory_id": memory_id,
"episode_logged": episode_logged,
"auto_edges": auto_edges,
}
) )
_trigger_git_sync()
result = {
"success": True,
"memory_id": memory_id,
"episode_logged": episode_logged,
"auto_edges": auto_edges,
}
if auto_edge_error:
result["auto_edge_error"] = auto_edge_error
return ok(result)
elif tool_name == "memory_recall": elif tool_name == "memory_recall":
results = client.recall( results = client.recall(