synapsemem-ai 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: synapsemem-ai
3
+ Version: 0.1.0
4
+ Summary: A zero-overhead, temporal belief-graph memory engine for local AI agents.
5
+ Author: Rohan Dhar
6
+ Project-URL: Homepage, https://github.com/rohandhar6824/SynapseMem
7
+ Project-URL: Bug Tracker, https://github.com/rohandhar6824/SynapseMem/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: ollama>=0.1.0
16
+ Dynamic: license-file
17
+
18
+ # ⚡ SynapseMem
19
+
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
21
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
22
+ [![Hardware](https://img.shields.io/badge/Hardware-Edge%20%2F%20CPU%20Friendly-green)](#benchmarks)
23
+
24
+ **SynapseMem** is a lightweight, zero-dependency, temporal belief-graph memory engine designed to eliminate **chronological amnesia** and context bloat in small, local language models (SLMs).
25
+
26
+ Traditional Vector RAG dumps conflicting raw chunks into the prompt context, degrading accuracy and slowing down CPU inference. SynapseMem converts interactions into atomic beliefs, deterministically resolves state transitions over time, and injects clean, conflict-free context into edge models.
27
+
28
+ ---
29
+
30
+ ## 🔬 The Problem: Chronological Amnesia in RAG
31
+
32
+ When an autonomous agent runs over days or weeks, states drift:
33
+ * **Monday:** `"The production server is on port 8000."`
34
+ * **Friday:** `"We migrated the server to port 9090."`
35
+
36
+ Standard vector similarity retrieves **both** chunks with equal semantic weight. A 3B parameter model given both inputs often hallucinates stale data.
@@ -0,0 +1,19 @@
1
+ # ⚡ SynapseMem
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
4
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
5
+ [![Hardware](https://img.shields.io/badge/Hardware-Edge%20%2F%20CPU%20Friendly-green)](#benchmarks)
6
+
7
+ **SynapseMem** is a lightweight, zero-dependency, temporal belief-graph memory engine designed to eliminate **chronological amnesia** and context bloat in small, local language models (SLMs).
8
+
9
+ Traditional Vector RAG dumps conflicting raw chunks into the prompt context, degrading accuracy and slowing down CPU inference. SynapseMem converts interactions into atomic beliefs, deterministically resolves state transitions over time, and injects clean, conflict-free context into edge models.
10
+
11
+ ---
12
+
13
+ ## 🔬 The Problem: Chronological Amnesia in RAG
14
+
15
+ When an autonomous agent runs over days or weeks, states drift:
16
+ * **Monday:** `"The production server is on port 8000."`
17
+ * **Friday:** `"We migrated the server to port 9090."`
18
+
19
+ Standard vector similarity retrieves **both** chunks with equal semantic weight. A 3B parameter model given both inputs often hallucinates stale data.
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "synapsemem-ai"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name="Rohan Dhar" }
10
+ ]
11
+ description = "A zero-overhead, temporal belief-graph memory engine for local AI agents."
12
+ readme = "README.md"
13
+ requires-python = ">=3.9"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
19
+ ]
20
+ dependencies = [
21
+ "ollama>=0.1.0",
22
+ ]
23
+
24
+ [project.urls]
25
+ "Homepage" = "https://github.com/rohandhar6824/SynapseMem"
26
+ "Bug Tracker" = "https://github.com/rohandhar6824/SynapseMem/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .engine import SynapseMemoryEngine, BeliefNode
2
+
3
+ __all__ = ["SynapseMemoryEngine", "BeliefNode"]
@@ -0,0 +1,119 @@
1
+ import ollama
2
+ import json
3
+ import time
4
+ import re
5
+ from synapse_core import SynapseMemoryEngine
6
+
7
+ memory = SynapseMemoryEngine()
8
+ MODEL = "qwen2.5:3b"
9
+
10
+ QUESTION_STARTERS = (
11
+ "what", "when", "where", "which", "who", "whom", "whose",
12
+ "why", "how", "is", "are", "was", "were", "do", "does",
13
+ "did", "can", "could", "should", "would", "tell me"
14
+ )
15
+
16
+ def is_query_only(text: str) -> bool:
17
+ """Detects if an input is purely a question to avoid state poisoning."""
18
+ cleaned = text.strip().lower()
19
+ if cleaned.endswith("?"):
20
+ return True
21
+ return any(cleaned.startswith(w + " ") or cleaned == w for w in QUESTION_STARTERS)
22
+
23
+ def extract_triplets(user_text: str) -> list:
24
+ """Extracts facts only from declarative statements."""
25
+ system_prompt = (
26
+ "Extract new factual declarations from the user text as JSON: {\"facts\": [{\"subject\": ..., \"predicate\": ..., \"object\": ...}]}.\n"
27
+ "RULES:\n"
28
+ "- Only extract facts if the user is making a clear, declarative statement.\n"
29
+ "- If the text is a question, query, or lacks concrete statements, return {\"facts\": []}.\n"
30
+ "- Keep predicates short (e.g., 'uses_port', 'has_status')."
31
+ )
32
+
33
+ try:
34
+ response = ollama.chat(
35
+ model=MODEL,
36
+ messages=[
37
+ {'role': 'system', 'content': system_prompt},
38
+ {'role': 'user', 'content': user_text}
39
+ ],
40
+ format='json'
41
+ )
42
+ data = json.loads(response['message']['content'])
43
+
44
+ if isinstance(data, dict):
45
+ for key in ['facts', 'triplets', 'data', 'items']:
46
+ if key in data and isinstance(data[key], list):
47
+ return data[key]
48
+ if 'subject' in data and 'predicate' in data and 'object' in data:
49
+ return [data]
50
+ return []
51
+ elif isinstance(data, list):
52
+ return data
53
+ return []
54
+ except Exception as e:
55
+ print(f"[Extractor Debug] Parse error: {e}")
56
+ return []
57
+
58
+ def chat_with_memory():
59
+ print(f"SynapseMem Agent Online using {MODEL}. Type 'exit' to quit.\n" + "="*55)
60
+
61
+ while True:
62
+ user_input = input("\nYou: ").strip()
63
+ if not user_input:
64
+ continue
65
+ if user_input.lower() in ['exit', 'quit']:
66
+ break
67
+
68
+ t_now = time.time()
69
+
70
+ # 1. State Mutation Gate: Only extract if it is NOT a read-only query
71
+ if is_query_only(user_input):
72
+ print("[Read-Only Query: Memory ingestion skipped]")
73
+ else:
74
+ triplets = extract_triplets(user_input)
75
+ if triplets:
76
+ print(f"[Extracted {len(triplets)} fact(s)]")
77
+ for t in triplets:
78
+ if isinstance(t, dict) and all(k in t for k in ['subject', 'predicate', 'object']):
79
+ print(" " + memory.ingest_belief(str(t['subject']), str(t['predicate']), str(t['object']), now=t_now))
80
+ else:
81
+ print("[Extracted 0 facts]")
82
+
83
+ # 2. Context Retrieval (Strip punctuation)
84
+ tokens = re.findall(r'\b[a-zA-Z0-9_]+\b', user_input.lower())
85
+ retrieved_segments = []
86
+ for token in tokens:
87
+ if len(token) >= 3:
88
+ ctx = memory.retrieve_active_context(token, now=t_now)
89
+ if "(None)" not in ctx:
90
+ retrieved_segments.append(ctx)
91
+
92
+ injected_context = "\n\n".join(sorted(set(retrieved_segments)))
93
+
94
+ # 3. Prompt Construction
95
+ if injected_context:
96
+ prompt = (
97
+ f"Use this verified system memory to answer the user query.\n\n"
98
+ f"{injected_context}\n\n"
99
+ f"User Question: {user_input}"
100
+ )
101
+ else:
102
+ prompt = user_input
103
+
104
+ print("\n[Thinking with Context...]")
105
+ response = ollama.chat(
106
+ model=MODEL,
107
+ messages=[
108
+ {
109
+ 'role': 'system',
110
+ 'content': "You are an accurate technical assistant. Rely strictly on the provided verified system memory for factual questions."
111
+ },
112
+ {'role': 'user', 'content': prompt}
113
+ ]
114
+ )
115
+
116
+ print(f"\nAgent: {response['message']['content']}")
117
+
118
+ if __name__ == "__main__":
119
+ chat_with_memory()
@@ -0,0 +1,140 @@
1
+ import time
2
+ import math
3
+ import sqlite3
4
+ from dataclasses import dataclass
5
+ from typing import Dict, List, Optional, Tuple
6
+
7
+ @dataclass
8
+ class BeliefNode:
9
+ subject: str
10
+ predicate: str
11
+ obj: str
12
+ created_at: float
13
+ last_accessed: float
14
+ strength: float = 1.0
15
+ is_active: bool = True
16
+ superseded_by: Optional[str] = None
17
+
18
+ def retention_score(self, current_time: float, decay_rate: float = 0.05) -> float:
19
+ if not self.is_active:
20
+ return 0.0
21
+ elapsed_hours = max(0.0, (current_time - self.last_accessed) / 3600.0)
22
+ effective_stability = self.strength / decay_rate
23
+ return math.exp(-elapsed_hours / effective_stability)
24
+
25
+
26
+ class SynapseMemoryEngine:
27
+ def __init__(self, db_path: str = "synapse_memory.db", plural_predicates: Optional[set] = None):
28
+ self.db_path = db_path
29
+ self.plural_predicates = plural_predicates or {
30
+ "has_tag", "contains", "includes", "depends_on", "has_member", "features"
31
+ }
32
+ self._init_db()
33
+
34
+ def _init_db(self):
35
+ """Initializes the persistent SQLite schema."""
36
+ with sqlite3.connect(self.db_path) as conn:
37
+ cursor = conn.cursor()
38
+ cursor.execute("""
39
+ CREATE TABLE IF NOT EXISTS beliefs (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ subject TEXT NOT NULL,
42
+ predicate TEXT NOT NULL,
43
+ object TEXT NOT NULL,
44
+ created_at REAL NOT NULL,
45
+ last_accessed REAL NOT NULL,
46
+ strength REAL NOT NULL,
47
+ is_active INTEGER NOT NULL,
48
+ superseded_by TEXT
49
+ )
50
+ """)
51
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_subj_pred ON beliefs(subject, predicate)")
52
+ conn.commit()
53
+
54
+ def _is_singular(self, predicate: str) -> bool:
55
+ if predicate in self.plural_predicates:
56
+ return False
57
+ state_prefixes = ("is", "has_", "uses_", "deployed_", "current_", "set_")
58
+ return predicate.startswith(state_prefixes) or predicate.endswith(("_status", "_port", "_id"))
59
+
60
+ def ingest_belief(self, subject: str, predicate: str, obj: str, now: Optional[float] = None) -> str:
61
+ now = now or time.time()
62
+ subj_norm = subject.strip().lower()
63
+ pred_norm = predicate.strip().lower()
64
+ obj_norm = obj.strip()
65
+ is_singular = self._is_singular(pred_norm)
66
+
67
+ with sqlite3.connect(self.db_path) as conn:
68
+ cursor = conn.cursor()
69
+
70
+ # Check for existing active beliefs
71
+ cursor.execute("""
72
+ SELECT id, object, strength FROM beliefs
73
+ WHERE subject = ? AND predicate = ? AND is_active = 1
74
+ """, (subj_norm, pred_norm))
75
+ active_rows = cursor.fetchall()
76
+
77
+ for row_id, existing_obj, strength in active_rows:
78
+ # Reinforce exact match
79
+ if existing_obj.lower() == obj_norm.lower():
80
+ new_strength = strength + 1.0
81
+ cursor.execute("""
82
+ UPDATE beliefs SET last_accessed = ?, strength = ? WHERE id = ?
83
+ """, (now, new_strength, row_id))
84
+ conn.commit()
85
+ return f"[REINFORCED] ({subj_norm}, {pred_norm}, {existing_obj}) | Strength: {new_strength:.1f}"
86
+
87
+ # Supersede old value if singular
88
+ elif is_singular:
89
+ cursor.execute("""
90
+ UPDATE beliefs SET is_active = 0, superseded_by = ? WHERE id = ?
91
+ """, (obj_norm, row_id))
92
+
93
+ # Insert new active belief
94
+ cursor.execute("""
95
+ INSERT INTO beliefs (subject, predicate, object, created_at, last_accessed, strength, is_active, superseded_by)
96
+ VALUES (?, ?, ?, ?, ?, 1.0, 1, NULL)
97
+ """, (subj_norm, pred_norm, obj_norm, now, now))
98
+ conn.commit()
99
+ return f"[COMMITTED] ({subj_norm}, {pred_norm}, {obj_norm})"
100
+
101
+ def retrieve_active_context(self, entity_query: str, min_retention: float = 0.2, now: Optional[float] = None) -> str:
102
+ now = now or time.time()
103
+ query_norm = entity_query.strip().lower()
104
+ active_facts = []
105
+ historical_shifts = []
106
+
107
+ with sqlite3.connect(self.db_path) as conn:
108
+ cursor = conn.cursor()
109
+ # Find any records touching this entity
110
+ cursor.execute("""
111
+ SELECT id, subject, predicate, object, created_at, last_accessed, strength, is_active, superseded_by
112
+ FROM beliefs
113
+ WHERE subject LIKE ? OR object LIKE ?
114
+ """, (f"%{query_norm}%", f"%{query_norm}%"))
115
+ rows = cursor.fetchall()
116
+
117
+ for row in rows:
118
+ r_id, subj, pred, obj_val, c_at, l_acc, strength, is_active, superseded = row
119
+ node = BeliefNode(subj, pred, obj_val, c_at, l_acc, strength, bool(is_active), superseded)
120
+
121
+ if node.is_active:
122
+ score = node.retention_score(now)
123
+ if score >= min_retention:
124
+ # Touch access time in DB
125
+ cursor.execute("UPDATE beliefs SET last_accessed = ? WHERE id = ?", (now, r_id))
126
+ active_facts.append(f"- {subj} {pred} {obj_val}")
127
+ elif node.superseded_by:
128
+ historical_shifts.append(f"- [PAST] {subj} {pred} was '{obj_val}' (now '{node.superseded_by}')")
129
+ conn.commit()
130
+
131
+ context_lines = ["### VERIFIED ACTIVE MEMORY:"] + (active_facts or ["(None)"])
132
+ if historical_shifts:
133
+ context_lines += ["\n### RESOLVED STATE CHANGES (DO NOT USE OLD VALUES):"] + historical_shifts[-4:]
134
+ return "\n".join(context_lines)
135
+
136
+ def reset_memory(self):
137
+ """Utility to wipe database for clean benchmarking."""
138
+ with sqlite3.connect(self.db_path) as conn:
139
+ conn.cursor().execute("DROP TABLE IF EXISTS beliefs")
140
+ self._init_db()
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: synapsemem-ai
3
+ Version: 0.1.0
4
+ Summary: A zero-overhead, temporal belief-graph memory engine for local AI agents.
5
+ Author: Rohan Dhar
6
+ Project-URL: Homepage, https://github.com/rohandhar6824/SynapseMem
7
+ Project-URL: Bug Tracker, https://github.com/rohandhar6824/SynapseMem/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: ollama>=0.1.0
16
+ Dynamic: license-file
17
+
18
+ # ⚡ SynapseMem
19
+
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
21
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
22
+ [![Hardware](https://img.shields.io/badge/Hardware-Edge%20%2F%20CPU%20Friendly-green)](#benchmarks)
23
+
24
+ **SynapseMem** is a lightweight, zero-dependency, temporal belief-graph memory engine designed to eliminate **chronological amnesia** and context bloat in small, local language models (SLMs).
25
+
26
+ Traditional Vector RAG dumps conflicting raw chunks into the prompt context, degrading accuracy and slowing down CPU inference. SynapseMem converts interactions into atomic beliefs, deterministically resolves state transitions over time, and injects clean, conflict-free context into edge models.
27
+
28
+ ---
29
+
30
+ ## 🔬 The Problem: Chronological Amnesia in RAG
31
+
32
+ When an autonomous agent runs over days or weeks, states drift:
33
+ * **Monday:** `"The production server is on port 8000."`
34
+ * **Friday:** `"We migrated the server to port 9090."`
35
+
36
+ Standard vector similarity retrieves **both** chunks with equal semantic weight. A 3B parameter model given both inputs often hallucinates stale data.
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ synapsemem/__init__.py
5
+ synapsemem/bridge.py
6
+ synapsemem/engine.py
7
+ synapsemem_ai.egg-info/PKG-INFO
8
+ synapsemem_ai.egg-info/SOURCES.txt
9
+ synapsemem_ai.egg-info/dependency_links.txt
10
+ synapsemem_ai.egg-info/requires.txt
11
+ synapsemem_ai.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ ollama>=0.1.0
@@ -0,0 +1 @@
1
+ synapsemem