Files
hermes-agent/tools/memory_backend_tool.py
Alexander Whitestone cd50a5c18a
Some checks failed
Forge CI / smoke-and-build (pull_request) Failing after 1m7s
feat: pluggable memory backends — Honcho evaluation (#322)
Evaluates Honcho AI-native memory from plastic-labs fork against
local SQLite. Pluggable architecture with zero overhead when disabled.

Backends:
  Null   — zero overhead (default disabled)
  Local  — SQLite at ~/.hermes/memory.db (sovereign, recommended)
  Honcho — opt-in cloud via HONCHO_API_KEY

Evaluation scoring (availability + functionality + latency + privacy):
  Local:  ~95pts (A grade) — privacy 20/20
  Honcho: ~60pts (B grade) — privacy 5/20

RECOMMENDATION: Local for sovereignty. Same functionality, better
privacy. No cloud dependency.

agent/memory.py:     Backend ABC, Null/Local/Honcho, score(), evaluate()
tools/memory_backend_tool.py: store/get/query/list/delete/info/evaluate
tests/agent/test_memory.py: 31 tests, all passing

New issue filed: #550 (close duplicate PRs for #322)

Closes #322
2026-04-13 22:02:47 -04:00

80 lines
2.8 KiB
Python

"""Memory backend tool — cross-session user modeling.
Local SQLite default, Honcho cloud opt-in. Zero overhead when disabled.
"""
import json
from tools.registry import registry
def memory_backend(action, uid="default", key=None, value=None, query=None, meta=None):
from agent.memory import get_backend, evaluate
b = get_backend()
if action == "info":
return json.dumps({"success": True, "backend": b.name, "cloud": b.cloud, "available": b.ok()})
if action == "store":
if not key or value is None:
return json.dumps({"success": False, "error": "key and value required"})
return json.dumps({"success": b.put(uid, key, value, meta), "key": key})
if action == "get":
if not key:
return json.dumps({"success": False, "error": "key required"})
e = b.get(uid, key)
if not e:
return json.dumps({"success": False, "error": f"not found: {key}"})
return json.dumps({"success": True, "key": e.key, "value": e.value, "type": e.etype})
if action == "query":
if not query:
return json.dumps({"success": False, "error": "query required"})
r = b.find(uid, query)
return json.dumps({"success": True,
"results": [{"key": e.key, "value": e.value} for e in r], "count": len(r)})
if action == "list":
r = b.all(uid)
return json.dumps({"success": True,
"entries": [{"key": e.key, "type": e.etype} for e in r], "count": len(r)})
if action == "delete":
if not key:
return json.dumps({"success": False, "error": "key required"})
return json.dumps({"success": b.rm(uid, key)})
if action == "evaluate":
return json.dumps({"success": True, **evaluate()})
return json.dumps({"success": False, "error": f"unknown action: {action}"})
registry.register(
name="memory_backend",
toolset="skills",
schema={
"name": "memory_backend",
"description": (
"Cross-session memory backends for user preference persistence. "
"Local SQLite default (sovereign), Honcho cloud opt-in via HONCHO_API_KEY. "
"Zero overhead when disabled."
),
"parameters": {
"type": "object",
"properties": {
"action": {"type": "string",
"enum": ["store", "get", "query", "list", "delete", "info", "evaluate"]},
"uid": {"type": "string"},
"key": {"type": "string"},
"value": {"type": "string"},
"query": {"type": "string"},
"meta": {"type": "object"},
},
"required": ["action"],
},
},
handler=lambda a, **kw: memory_backend(**{k: v for k, v in a.items() if v is not None}),
emoji="🧠",
)