Spaces:
Sleeping
Sleeping
| """ | |
| 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:] | |