Umbra-Meta / memory_module.py
amrita8642's picture
Deploy: full source without binary graph files
6223201
"""
UMBRA episodic memory — persists cross-episode learning context for the agent.
Last 5 episodes injected as memory_context string into each new episode's observation.
Local JSON storage only. No network. No external DB.
"""
import json
import os
from pathlib import Path
EPISODES_FILE = Path("logs/episodes.jsonl")
class MemoryModule:
def __init__(self):
EPISODES_FILE.parent.mkdir(parents=True, exist_ok=True)
self._episode_id = self._count_episodes()
def _count_episodes(self) -> int:
if not EPISODES_FILE.exists():
return 0
with open(EPISODES_FILE) as f:
return sum(1 for _ in f)
def log_episode(self, state: dict, actions: list[int], total_reward: float, sentrix_blocks: int) -> None:
record = {
"episode_id": self._episode_id,
"stage": state.get("current_stage", 1),
"active_npcs": state.get("active_npcs", []),
"actions_taken": actions,
"signals_fired": [],
"reward_breakdown": {},
"verifier_flags": [],
"total_reward": total_reward,
"sentrix_blocks": sentrix_blocks,
"sentrix_false_positives": 0,
}
with open(EPISODES_FILE, "a") as f:
f.write(json.dumps(record) + "\n")
self._episode_id += 1
def get_context(self) -> str:
if not EPISODES_FILE.exists():
return "No prior episodes."
episodes = []
with open(EPISODES_FILE) as f:
for line in f:
try:
episodes.append(json.loads(line.strip()))
except json.JSONDecodeError:
continue
last5 = episodes[-5:]
if not last5:
return "No prior episodes."
lines = []
for ep in last5:
npcs = ", ".join(ep.get("active_npcs", []))
reward = ep.get("total_reward", 0.0)
blocks = ep.get("sentrix_blocks", 0)
lines.append(f"Episode {ep['episode_id']}: npcs=[{npcs}] reward={reward:.2f} sentrix_blocks={blocks}")
return " | ".join(lines)
def load_last_n(self, n: int = 5) -> list[dict]:
if not EPISODES_FILE.exists():
return []
episodes = []
with open(EPISODES_FILE) as f:
for line in f:
try:
episodes.append(json.loads(line.strip()))
except json.JSONDecodeError:
continue
return episodes[-n:]