1  #!/usr/bin/env python3
   2  """
   3  Graph-Weighted Memory Engine for Distributed Cognitive Cluster
   4  Implements Dynamic Edge Reinforcement, Decay, and Spreading Activation
   5  """
   6  import sqlite3
   7  import datetime
   8  import math
   9  import sys
  10  import os
  11  import json
  12  
  13  DB_PATH = os.environ.get(
  14      'GRAPH_MEMORY_DB',
  15      '/var/neural_memory/data/graph_memory.db' if os.path.exists('/var/neural_memory/data/graph_memory.db')
  16      else os.path.join(os.path.dirname(os.path.abspath(__file__)), '.graph_memory_cache.db')
  17  )
  18  
  19  def get_db():
  20      conn = sqlite3.connect(DB_PATH)
  21      conn.execute('PRAGMA journal_mode = WAL;')
  22      conn.execute('PRAGMA foreign_keys = ON;')
  23      conn.row_factory = sqlite3.Row
  24      return conn
  25  
  26  def init_db():
  27      conn = get_db()
  28      c = conn.cursor()
  29      c.executescript('''
  30      CREATE TABLE IF NOT EXISTS graph_nodes (
  31          id TEXT PRIMARY KEY,
  32          label TEXT NOT NULL,
  33          entity_type TEXT NOT NULL,
  34          content TEXT NOT NULL,
  35          embedding BLOB,
  36          importance_score REAL DEFAULT 0.50,
  37          access_count INTEGER DEFAULT 1,
  38          created_at TEXT NOT NULL,
  39          last_accessed_at TEXT NOT NULL
  40      );
  41  
  42      CREATE TABLE IF NOT EXISTS graph_edges (
  43          source_id TEXT NOT NULL,
  44          target_id TEXT NOT NULL,
  45          relation TEXT NOT NULL,
  46          weight REAL DEFAULT 0.80,
  47          confidence REAL DEFAULT 1.00,
  48          last_reinforced_at TEXT NOT NULL,
  49          decay_rate REAL DEFAULT 0.005,
  50          PRIMARY KEY (source_id, target_id, relation),
  51          FOREIGN KEY (source_id) REFERENCES graph_nodes(id) ON DELETE CASCADE,
  52          FOREIGN KEY (target_id) REFERENCES graph_nodes(id) ON DELETE CASCADE
  53      );
  54  
  55      CREATE TABLE IF NOT EXISTS graph_episodes (
  56          id INTEGER PRIMARY KEY AUTOINCREMENT,
  57          title TEXT NOT NULL,
  58          context TEXT NOT NULL,
  59          actions TEXT NOT NULL,
  60          outcome TEXT NOT NULL,
  61          root_node_id TEXT,
  62          timestamp TEXT NOT NULL,
  63          FOREIGN KEY (root_node_id) REFERENCES graph_nodes(id) ON DELETE SET NULL
  64      );
  65  
  66      CREATE TABLE IF NOT EXISTS graph_skills (
  67          name TEXT PRIMARY KEY,
  68          trigger_pattern TEXT NOT NULL,
  69          command_template TEXT NOT NULL,
  70          description TEXT NOT NULL,
  71          associated_node_id TEXT,
  72          FOREIGN KEY (associated_node_id) REFERENCES graph_nodes(id) ON DELETE SET NULL
  73      );
  74  
  75      CREATE INDEX IF NOT EXISTS idx_nodes_type ON graph_nodes(entity_type);
  76      CREATE INDEX IF NOT EXISTS idx_edges_source ON graph_edges(source_id);
  77      CREATE INDEX IF NOT EXISTS idx_edges_target ON graph_edges(target_id);
  78      ''')
  79      conn.commit()
  80      conn.close()
  81  
  82  def now_iso():
  83      return datetime.datetime.now(datetime.timezone.utc).isoformat()
  84  
  85  def add_node(node_id, label, entity_type, content, importance=0.5):
  86      conn = get_db()
  87      c = conn.cursor()
  88      t = now_iso()
  89      c.execute('''
  90      INSERT INTO graph_nodes (id, label, entity_type, content, importance_score, access_count, created_at, last_accessed_at)
  91      VALUES (?, ?, ?, ?, ?, 1, ?, ?)
  92      ON CONFLICT(id) DO UPDATE SET
  93          label=excluded.label,
  94          entity_type=excluded.entity_type,
  95          content=excluded.content,
  96          importance_score=excluded.importance_score,
  97          access_count=access_count+1,
  98          last_accessed_at=excluded.last_accessed_at;
  99      ''', (node_id, label, entity_type, content, importance, t, t))
 100      conn.commit()
 101      conn.close()
 102  
 103  def add_edge(source_id, target_id, relation, weight=0.8, confidence=1.0):
 104      conn = get_db()
 105      c = conn.cursor()
 106      t = now_iso()
 107      c.execute('''
 108      INSERT INTO graph_edges (source_id, target_id, relation, weight, confidence, last_reinforced_at)
 109      VALUES (?, ?, ?, ?, ?, ?)
 110      ON CONFLICT(source_id, target_id, relation) DO UPDATE SET
 111          weight=MIN(1.0, weight + 0.15),
 112          last_reinforced_at=excluded.last_reinforced_at;
 113      ''', (source_id, target_id, relation, weight, confidence, t))
 114      conn.commit()
 115      conn.close()
 116  
 117  def spread_activation(seed_id, max_depth=2, decay=0.75, threshold=0.25):
 118      conn = get_db()
 119      c = conn.cursor()
 120      
 121      visited_nodes = {}
 122      visited_edges = []
 123      queue = [(seed_id, 1.0, 0)] # (node_id, energy, depth)
 124      
 125      while queue:
 126          curr_id, energy, depth = queue.pop(0)
 127          if curr_id in visited_nodes and visited_nodes[curr_id] >= energy:
 128              continue
 129          visited_nodes[curr_id] = energy
 130          
 131          # Update last accessed
 132          c.execute('UPDATE graph_nodes SET access_count=access_count+1, last_accessed_at=? WHERE id=?', (now_iso(), curr_id))
 133          
 134          if depth >= max_depth:
 135              continue
 136              
 137          c.execute('SELECT target_id, relation, weight FROM graph_edges WHERE source_id=?', (curr_id,))
 138          for target_id, rel, weight in c.fetchall():
 139              next_energy = energy * weight * decay
 140              if next_energy >= threshold:
 141                  visited_edges.append({'from': curr_id, 'rel': rel, 'to': target_id, 'weight': round(weight, 3)})
 142                  queue.append((target_id, next_energy, depth + 1))
 143                  # Reinforce edge on traversal
 144                  c.execute('UPDATE graph_edges SET weight=MIN(1.0, weight+0.05), last_reinforced_at=? WHERE source_id=? AND target_id=? AND relation=?',
 145                            (now_iso(), curr_id, target_id, rel))
 146                  
 147      conn.commit()
 148      
 149      # Fetch node details
 150      nodes_data = []
 151      for nid, energy in sorted(visited_nodes.items(), key=lambda x: x[1], reverse=True):
 152          c.execute('SELECT id, label, entity_type, content, importance_score FROM graph_nodes WHERE id=?', (nid,))
 153          row = c.fetchone()
 154          if row:
 155              d = dict(row)
 156              d['activation_energy'] = round(energy, 3)
 157              nodes_data.append(d)
 158              
 159      conn.close()
 160      return {'nodes': nodes_data, 'edges': visited_edges}
 161  
 162  def seed_baseline():
 163      init_db()
 164      # 1. Physical nodes
 165      nodes = [
 166          ("node_master", "Apple Silicon Mac (Master)", "host", "Primary cognitive controller, runs DeepSeek-R1 inference on Metal GPU.", 1.0),
 167          ("node_dell", "Dell Inspiron 3521 (Tactical Body)", "host", "24/7 headless execution worker, persistent memory anchor, mass storage server.", 1.0),
 168          ("vol_ssd", "Internal ADATA SSD (/dev/sda3)", "storage", "146 GB ext4, root OS volume and L1 hot memory graph anchor.", 0.95),
 169          ("vol_d_drive", "Protected Personal Drive D (/dev/sda5)", "storage", "77.1 GB NTFS, contains critical non-recoverable operator data. ROE-01 Enforced.", 1.0),
 170          ("vol_hdd1", "Mass Storage HDD 1 (/dev/sdb1)", "storage", "931.5 GB NTFS mounted at /mnt/hdd1 (L2 warm semantic vector store).", 0.90),
 171          ("vol_hdd2", "Mass Storage HDD 2 (/dev/sdc1)", "storage", "931.5 GB Mass storage mounted at /mnt/hdd2 (L3 cold archival vault).", 0.90),
 172          ("hw_gpu_amd", "AMD Radeon HD 8730M", "hardware", "Discrete GPU with 2GB VRAM, driver amdgpu, render node /dev/dri/renderD129.", 0.85),
 173          ("hw_gpu_intel", "Intel HD Graphics 4000", "hardware", "Integrated display core, driver i915, render node /dev/dri/renderD128.", 0.70),
 174          ("hw_cpu", "Intel Core i5-3337U", "hardware", "Ivy Bridge, 2C/4T, 1.80-2.70GHz, AVX, VT-x active.", 0.85),
 175          ("rule_roe01", "ROE-01: Sanctity of Drive D", "policy", "Under no circumstances may /dev/sda5 be modified, partitioned, or written to. Read-only only.", 1.0),
 176          ("rule_caffeine", "Power: Permanent Caffeinate Policy", "policy", "Systemd sleep, suspend, and hibernate targets masked to /dev/null; wifi powersave disabled.", 0.95),
 177          ("skill_caffeinate", "caffeinate", "skill", "sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target", 0.90),
 178          ("skill_test_gpu", "test_gpu", "skill", "cat /sys/class/drm/card1/device/mem_info_vram_total", 0.80),
 179          ("picoclaw_daemon", "PicoClaw Gateway", "agent", "Ultra-lightweight Go agent daemon running 24/7 in under 10MB RAM on Dell.", 0.95)
 180      ]
 181      for nid, lbl, etype, cnt, imp in nodes:
 182          add_node(nid, lbl, etype, cnt, imp)
 183  
 184      # 2. Directed weighted edges
 185      edges = [
 186          ("node_master", "node_dell", "COMMANDS", 1.0),
 187          ("node_dell", "vol_ssd", "CONTAINS", 1.0),
 188          ("node_dell", "vol_d_drive", "PROTECTS", 1.0),
 189          ("node_dell", "vol_hdd1", "MOUNTS", 0.95),
 190          ("node_dell", "vol_hdd2", "MOUNTS", 0.95),
 191          ("node_dell", "hw_gpu_amd", "CONTAINS", 0.95),
 192          ("node_dell", "hw_gpu_intel", "CONTAINS", 0.80),
 193          ("node_dell", "hw_cpu", "CONTAINS", 0.95),
 194          ("node_dell", "picoclaw_daemon", "HOSTS", 1.0),
 195          ("rule_roe01", "vol_d_drive", "GOVERNS", 1.0),
 196          ("rule_caffeine", "node_dell", "PROTECTS", 1.0),
 197          ("skill_caffeinate", "rule_caffeine", "IMPLEMENTS", 0.95),
 198          ("skill_test_gpu", "hw_gpu_amd", "AUDITS", 0.90)
 199      ]
 200      for s, t, r, w in edges:
 201          add_edge(s, t, r, w)
 202  
 203  if __name__ == '__main__':
 204      if len(sys.argv) > 1 and sys.argv[1] == 'seed':
 205          seed_baseline()
 206          print('GRAPH_MEMORY_INITIALIZED_AND_SEEDED')
 207      elif len(sys.argv) > 2 and sys.argv[1] == 'query':
 208          subgraph = spread_activation(sys.argv[2])
 209          print(json.dumps(subgraph, indent=2))
 210      elif len(sys.argv) > 1 and sys.argv[1] == 'status':
 211          conn = get_db()
 212          c = conn.cursor()
 213          c.execute('SELECT COUNT(*) FROM graph_nodes;')
 214          nn = c.fetchone()[0]
 215          c.execute('SELECT COUNT(*) FROM graph_edges;')
 216          ne = c.fetchone()[0]
 217          conn.close()
 218          print(f"Graph Status: {nn} Nodes, {ne} Edges")
 219      else:
 220          init_db()
 221          print('GRAPH_DB_VERIFIED')
 222