FerrellSyntheticIntelligence
Vitalis LOREIN MCP Server — full 26-tool package with one-command launcher
35341c0
Raw
History Blame Contribute Delete
6.55 kB
"""
Hierarchical Memory — Episodic, Semantic, and Procedural tiers.
Episodic: raw event log (time-indexed)
Semantic: knowledge graph of Atomic Truths (distilled from episodic)
Procedural: learned skills and protocols
"""
import json
import time
from collections import defaultdict
from pathlib import Path
from typing import Any
from config.config import MEMORY_DIR
class EpisodicMemory:
def __init__(self, agent_id: str = "vitalis"):
self.path = MEMORY_DIR / f"{agent_id}_episodic.jsonl"
self._events: list[dict] = []
self._load()
def _load(self):
if self.path.exists():
with open(self.path) as f:
for line in f:
line = line.strip()
if line:
self._events.append(json.loads(line))
def record(self, event_type: str, content: dict[str, Any],
source: str = "perception"):
entry = {
"id": f"ep-{int(time.time() * 1000)}-{len(self._events)}",
"timestamp": time.time(),
"type": event_type,
"source": source,
"content": content,
}
self._events.append(entry)
with open(self.path, "a") as f:
f.write(json.dumps(entry) + "\n")
return entry
def query(self, query_type: str | None = None,
limit: int = 50, since: float = 0) -> list[dict]:
results = []
for e in reversed(self._events):
if e["timestamp"] < since:
continue
if query_type and e["type"] != query_type:
continue
results.append(e)
if len(results) >= limit:
break
return results
def recent(self, seconds: float = 300) -> list[dict]:
cutoff = time.time() - seconds
return self.query(since=cutoff)
def count(self) -> int:
return len(self._events)
class AtomicTruth:
def __init__(self, truth_id: str, statement: str, confidence: float,
source_events: list[str], category: str = "general"):
self.truth_id = truth_id
self.statement = statement
self.confidence = confidence
self.source_events = source_events
self.category = category
self.created_at = time.time()
self.access_count = 0
def to_dict(self) -> dict:
return {
"truth_id": self.truth_id,
"statement": self.statement,
"confidence": self.confidence,
"source_events": self.source_events,
"category": self.category,
"created_at": self.created_at,
"access_count": self.access_count,
}
@classmethod
def from_dict(cls, d: dict) -> "AtomicTruth":
t = cls(d["truth_id"], d["statement"], d["confidence"],
d["source_events"], d.get("category", "general"))
t.created_at = d.get("created_at", time.time())
t.access_count = d.get("access_count", 0)
return t
class SemanticMemory:
def __init__(self, agent_id: str = "vitalis"):
self.path = MEMORY_DIR / f"{agent_id}_semantic.json"
self._truths: dict[str, AtomicTruth] = {}
self._load()
def _load(self):
if self.path.exists():
data = json.loads(self.path.read_text())
for d in data:
truth = AtomicTruth.from_dict(d)
self._truths[truth.truth_id] = truth
def _save(self):
with open(self.path, "w") as f:
json.dump([t.to_dict() for t in self._truths.values()], f, indent=2)
def add(self, statement: str, confidence: float,
source_events: list[str], category: str = "general") -> AtomicTruth:
tid = f"at-{int(time.time())}-{hash(statement) % 10000}"
truth = AtomicTruth(tid, statement, confidence, source_events, category)
self._truths[tid] = truth
self._save()
return truth
def query(self, text: str, top_k: int = 5) -> list[AtomicTruth]:
text_lower = text.lower()
scored = []
for t in self._truths.values():
score = 0.0
if text_lower in t.statement.lower():
score += t.confidence * 0.8
words = set(text_lower.split())
truth_words = set(t.statement.lower().split())
overlap = len(words & truth_words) / max(len(words), 1)
score += overlap * 0.2
scored.append((score, t))
scored.sort(key=lambda x: -x[0])
results = [t for _, t in scored[:top_k]]
for t in results:
t.access_count += 1
self._save()
return results
def get_by_category(self, category: str) -> list[AtomicTruth]:
return [t for t in self._truths.values() if t.category == category]
def count(self) -> int:
return len(self._truths)
class ProceduralMemory:
def __init__(self, agent_id: str = "vitalis"):
self.path = MEMORY_DIR / f"{agent_id}_procedural.json"
self._skills: dict[str, dict] = {}
self._load()
def _load(self):
if self.path.exists():
self._skills = json.loads(self.path.read_text())
def _save(self):
with open(self.path, "w") as f:
json.dump(self._skills, f, indent=2)
def record_skill(self, name: str, description: str,
protocol: list[str], success_rate: float = 1.0):
self._skills[name] = {
"description": description,
"protocol": protocol,
"success_rate": success_rate,
"use_count": 0,
"created_at": time.time(),
}
self._save()
def retrieve(self, task: str) -> list[tuple[str, dict]]:
task_lower = task.lower()
results = []
for name, skill in self._skills.items():
if task_lower in name.lower() or task_lower in skill.get("description", "").lower():
results.append((name, skill))
results.sort(key=lambda x: -x[1].get("success_rate", 0))
return results
def record_success(self, name: str):
skill = self._skills.get(name)
if skill:
skill["use_count"] = skill.get("use_count", 0) + 1
self._save()
def record_failure(self, name: str):
skill = self._skills.get(name)
if skill:
skill["success_rate"] = max(0.0, skill.get("success_rate", 1.0) - 0.1)
self._save()
def count(self) -> int:
return len(self._skills)