memorycoreclaw 2.0.0__py3-none-any.whl

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.
@@ -0,0 +1,21 @@
1
+ # MemoryCoreClaw
2
+ # Human-brain-inspired Long-term Memory Engine for AI Agents
3
+
4
+ __version__ = "2.0.0"
5
+ __author__ = "MemoryCoreClaw Team"
6
+
7
+ from .core.memory import Memory, get_memory
8
+ from .core.engine import MemoryEngine, MemoryLayer, Emotion, Fact, Lesson, STANDARD_RELATIONS
9
+ from .cognitive.contextual import Context
10
+
11
+ __all__ = [
12
+ 'Memory',
13
+ 'MemoryEngine',
14
+ 'get_memory',
15
+ 'Context',
16
+ 'MemoryLayer',
17
+ 'Emotion',
18
+ 'Fact',
19
+ 'Lesson',
20
+ 'STANDARD_RELATIONS',
21
+ ]
@@ -0,0 +1,8 @@
1
+ """MemoryCoreClaw Cognitive Module"""
2
+
3
+ from .forgetting import ForgettingCurve
4
+ from .contextual import ContextualMemory
5
+ from .working import WorkingMemory
6
+ from .heuristic import HeuristicEngine
7
+
8
+ __all__ = ["ForgettingCurve", "ContextualMemory", "WorkingMemory", "HeuristicEngine"]
@@ -0,0 +1,100 @@
1
+ """
2
+ MemoryCoreClaw - Contextual Memory Module
3
+
4
+ Implements context-based memory recall.
5
+ """
6
+
7
+ from datetime import datetime
8
+ from typing import Optional, List, Dict, Any
9
+ from dataclasses import dataclass, field
10
+
11
+
12
+ @dataclass
13
+ class Context:
14
+ """Memory context"""
15
+ location: Optional[str] = None
16
+ people: List[str] = field(default_factory=list)
17
+ emotion: Optional[str] = None
18
+ activity: Optional[str] = None
19
+ channel: Optional[str] = None
20
+ timestamp: datetime = field(default_factory=datetime.now)
21
+
22
+
23
+ class ContextualMemory:
24
+ """
25
+ Contextual Memory Engine
26
+
27
+ Features:
28
+ - Bind memories to contexts
29
+ - Trigger recall by context
30
+ - Score context matches
31
+
32
+ Usage:
33
+ cm = ContextualMemory()
34
+ ctx = Context(location="office", people=["Alice"])
35
+ cm.bind("fact", 1, ctx)
36
+ results = cm.recall(people=["Alice"])
37
+ """
38
+
39
+ def __init__(self, db_connection=None):
40
+ self.conn = db_connection
41
+
42
+ def create_context(self, context: Context) -> int:
43
+ """Create a context record"""
44
+ # Implementation depends on database
45
+ pass
46
+
47
+ def bind_memory(self, memory_type: str, memory_id: int, context: Context) -> int:
48
+ """Bind a memory to a context"""
49
+ pass
50
+
51
+ def recall_by_context(
52
+ self,
53
+ location: str = None,
54
+ people: List[str] = None,
55
+ emotion: str = None,
56
+ activity: str = None
57
+ ) -> List[Dict]:
58
+ """Recall memories matching context"""
59
+ pass
60
+
61
+ def score_match(self, query_context: Context, memory_context: Context) -> float:
62
+ """
63
+ Score how well two contexts match.
64
+
65
+ Args:
66
+ query_context: The search context
67
+ memory_context: The memory's context
68
+
69
+ Returns:
70
+ Match score (0-1)
71
+ """
72
+ score = 0.0
73
+ max_score = 0.0
74
+
75
+ # Location match (weight: 1.0)
76
+ if query_context.location:
77
+ max_score += 1.0
78
+ if memory_context.location == query_context.location:
79
+ score += 1.0
80
+
81
+ # People overlap (weight: 0.8 per person)
82
+ if query_context.people:
83
+ for person in query_context.people:
84
+ max_score += 0.8
85
+ if person in memory_context.people:
86
+ score += 0.8
87
+
88
+ # Emotion match (weight: 0.5)
89
+ if query_context.emotion:
90
+ max_score += 0.5
91
+ if memory_context.emotion == query_context.emotion:
92
+ score += 0.5
93
+
94
+ # Activity match (weight: 0.7)
95
+ if query_context.activity:
96
+ max_score += 0.7
97
+ if memory_context.activity == query_context.activity:
98
+ score += 0.7
99
+
100
+ return score / max_score if max_score > 0 else 0.0
@@ -0,0 +1,162 @@
1
+ """
2
+ MemoryCoreClaw - Cognitive Module: Forgetting Curve
3
+
4
+ Implements Ebbinghaus forgetting curve for memory strength decay.
5
+ """
6
+
7
+ import math
8
+ from datetime import datetime, timedelta
9
+ from typing import Optional
10
+ import sqlite3
11
+
12
+
13
+ class ForgettingCurve:
14
+ """
15
+ Ebbinghaus forgetting curve implementation.
16
+
17
+ Formula: R = e^(-t/(S*100))
18
+ Where:
19
+ R = retention rate
20
+ t = time since last access (days)
21
+ S = memory strength (importance)
22
+
23
+ Features:
24
+ - Time-based decay
25
+ - Importance-based strength
26
+ - Emotion enhancement
27
+ - Access reinforcement
28
+ """
29
+
30
+ def __init__(self, db_path: str):
31
+ self.db_path = db_path
32
+ self._init_tables()
33
+
34
+ def _init_tables(self):
35
+ """Initialize memory strength table."""
36
+ conn = sqlite3.connect(self.db_path)
37
+ cursor = conn.cursor()
38
+
39
+ cursor.execute('''
40
+ CREATE TABLE IF NOT EXISTS memory_strength (
41
+ memory_id INTEGER PRIMARY KEY,
42
+ memory_type TEXT DEFAULT 'fact',
43
+ base_strength REAL DEFAULT 0.5,
44
+ current_strength REAL DEFAULT 0.5,
45
+ last_accessed TIMESTAMP,
46
+ access_count INTEGER DEFAULT 0,
47
+ decay_rate REAL DEFAULT 0.1
48
+ )
49
+ ''')
50
+
51
+ conn.commit()
52
+ conn.close()
53
+
54
+ def calculate_retention(self, days_since_access: float,
55
+ importance: float,
56
+ emotion: str = "neutral") -> float:
57
+ """
58
+ Calculate memory retention rate.
59
+
60
+ Args:
61
+ days_since_access: Days since last access
62
+ importance: Memory importance (0-1)
63
+ emotion: Emotional marker
64
+
65
+ Returns:
66
+ Retention rate (0-1)
67
+ """
68
+ # Base strength from importance
69
+ strength = importance
70
+
71
+ # Emotion enhancement
72
+ emotion_multiplier = {
73
+ "milestone": 1.5,
74
+ "negative": 1.3,
75
+ "positive": 1.2,
76
+ "neutral": 1.0
77
+ }
78
+ strength *= emotion_multiplier.get(emotion, 1.0)
79
+
80
+ # Ebbinghaus formula
81
+ if days_since_access <= 0:
82
+ return 1.0
83
+
84
+ retention = math.exp(-days_since_access / (strength * 100))
85
+
86
+ # Minimum retention
87
+ return max(0.1, min(1.0, retention))
88
+
89
+ def apply_forgetting_curve(self, min_importance: float = 0.3):
90
+ """
91
+ Apply forgetting curve to all memories.
92
+
93
+ Memories below min_importance will be candidates for removal.
94
+ """
95
+ conn = sqlite3.connect(self.db_path)
96
+ cursor = conn.cursor()
97
+
98
+ # Get all facts
99
+ cursor.execute('''
100
+ SELECT f.id, f.importance, f.emotion, f.last_accessed, f.created_at
101
+ FROM facts f
102
+ WHERE f.importance >= ?
103
+ ''', (min_importance,))
104
+
105
+ facts = cursor.fetchall()
106
+ updated = 0
107
+
108
+ for fact_id, importance, emotion, last_accessed, created_at in facts:
109
+ # Calculate days since access
110
+ if last_accessed:
111
+ last = datetime.fromisoformat(last_accessed)
112
+ elif created_at:
113
+ last = datetime.fromisoformat(created_at)
114
+ else:
115
+ last = datetime.now()
116
+
117
+ days_since = (datetime.now() - last).days
118
+
119
+ # Calculate new retention
120
+ retention = self.calculate_retention(days_since, importance, emotion or "neutral")
121
+
122
+ # Update strength
123
+ new_strength = importance * retention
124
+
125
+ # Core memories (importance >= 0.9) don't decay below 0.8
126
+ if importance >= 0.9:
127
+ new_strength = max(0.8, new_strength)
128
+
129
+ cursor.execute('''
130
+ INSERT OR REPLACE INTO memory_strength
131
+ (memory_id, memory_type, current_strength, last_accessed)
132
+ VALUES (?, 'fact', ?, ?)
133
+ ''', (fact_id, new_strength, datetime.now().isoformat()))
134
+
135
+ updated += 1
136
+
137
+ conn.commit()
138
+ conn.close()
139
+
140
+ return updated
141
+
142
+ def reinforce_memory(self, memory_id: int, factor: float = 1.1):
143
+ """
144
+ Reinforce memory strength (called on access).
145
+
146
+ Args:
147
+ memory_id: Memory ID
148
+ factor: Reinforcement factor (default 1.1 = 10% increase)
149
+ """
150
+ conn = sqlite3.connect(self.db_path)
151
+ cursor = conn.cursor()
152
+
153
+ cursor.execute('''
154
+ UPDATE memory_strength
155
+ SET current_strength = MIN(1.0, current_strength * ?),
156
+ access_count = access_count + 1,
157
+ last_accessed = ?
158
+ WHERE memory_id = ?
159
+ ''', (factor, datetime.now().isoformat(), memory_id))
160
+
161
+ conn.commit()
162
+ conn.close()
@@ -0,0 +1,217 @@
1
+ """
2
+ MemoryCoreClaw - Heuristic Engine Module
3
+
4
+ Implements cognitive schema recognition and thought patterns.
5
+ """
6
+
7
+ from typing import List, Dict, Optional
8
+ from dataclasses import dataclass
9
+ import re
10
+
11
+
12
+ @dataclass
13
+ class Schema:
14
+ """A cognitive schema pattern"""
15
+ name: str
16
+ description: str
17
+ patterns: List[str]
18
+ triggers: List[str]
19
+
20
+
21
+ class HeuristicEngine:
22
+ """
23
+ Cognitive Heuristic Engine
24
+
25
+ Recognizes thought patterns and cognitive schemas.
26
+
27
+ Usage:
28
+ he = HeuristicEngine()
29
+ schemas = he.recognize("Why did this happen? How can I fix it?")
30
+ for s in schemas:
31
+ print(f"Detected: {s.name}")
32
+ """
33
+
34
+ # Predefined cognitive schemas
35
+ DEFAULT_SCHEMAS = [
36
+ Schema(
37
+ name="problem_solving",
38
+ description="Identifying problem and seeking solution",
39
+ patterns=[
40
+ r"why\s+did",
41
+ r"how\s+can\s+i\s+fix",
42
+ r"what\s+caused",
43
+ r"how\s+to\s+solve"
44
+ ],
45
+ triggers=["why", "how", "fix", "solve", "problem"]
46
+ ),
47
+ Schema(
48
+ name="planning",
49
+ description="Planning future actions",
50
+ patterns=[
51
+ r"i\s+will",
52
+ r"we\s+plan\s+to",
53
+ r"scheduled\s+for",
54
+ r"next\s+step"
55
+ ],
56
+ triggers=["plan", "will", "schedule", "next", "tomorrow"]
57
+ ),
58
+ Schema(
59
+ name="learning",
60
+ description="Learning from experience",
61
+ patterns=[
62
+ r"i\s+learned",
63
+ r"the\s+lesson\s+is",
64
+ r"i\s+realized",
65
+ r"now\s+i\s+know"
66
+ ],
67
+ triggers=["learned", "lesson", "realized", "know", "understand"]
68
+ ),
69
+ Schema(
70
+ name="inquiry",
71
+ description="Asking questions to understand",
72
+ patterns=[
73
+ r"who\s+is",
74
+ r"what\s+is",
75
+ r"where\s+is",
76
+ r"when\s+did",
77
+ r"\?$"
78
+ ],
79
+ triggers=["who", "what", "where", "when", "?"]
80
+ ),
81
+ Schema(
82
+ name="reflection",
83
+ description="Reflecting on past events",
84
+ patterns=[
85
+ r"i\s+remember",
86
+ r"looking\s+back",
87
+ r"in\s+the\s+past",
88
+ r"previously"
89
+ ],
90
+ triggers=["remember", "past", "before", "previously"]
91
+ ),
92
+ Schema(
93
+ name="decision",
94
+ description="Making a decision",
95
+ patterns=[
96
+ r"i\s+decided",
97
+ r"i\s+chose",
98
+ r"the\s+best\s+option",
99
+ r"i\s+prefer"
100
+ ],
101
+ triggers=["decided", "chose", "option", "prefer", "better"]
102
+ ),
103
+ Schema(
104
+ name="emotion_expression",
105
+ description="Expressing emotions",
106
+ patterns=[
107
+ r"i\s+feel",
108
+ r"i\s+am\s+(happy|sad|angry|worried|excited)",
109
+ r"makes\s+me\s+feel"
110
+ ],
111
+ triggers=["feel", "happy", "sad", "angry", "worried", "excited"]
112
+ )
113
+ ]
114
+
115
+ def __init__(self, custom_schemas: List[Schema] = None):
116
+ """
117
+ Initialize heuristic engine.
118
+
119
+ Args:
120
+ custom_schemas: Additional schemas to include
121
+ """
122
+ self.schemas = self.DEFAULT_SCHEMAS.copy()
123
+ if custom_schemas:
124
+ self.schemas.extend(custom_schemas)
125
+
126
+ def recognize(self, text: str) -> List[Schema]:
127
+ """
128
+ Recognize cognitive schemas in text.
129
+
130
+ Args:
131
+ text: Input text
132
+
133
+ Returns:
134
+ List of matched schemas (sorted by relevance)
135
+ """
136
+ text_lower = text.lower()
137
+ matches = []
138
+
139
+ for schema in self.schemas:
140
+ score = 0
141
+
142
+ # Check patterns
143
+ for pattern in schema.patterns:
144
+ if re.search(pattern, text_lower):
145
+ score += 2
146
+
147
+ # Check triggers
148
+ for trigger in schema.triggers:
149
+ if trigger in text_lower:
150
+ score += 1
151
+
152
+ if score > 0:
153
+ matches.append((schema, score))
154
+
155
+ # Sort by score
156
+ matches.sort(key=lambda x: x[1], reverse=True)
157
+
158
+ return [m[0] for m in matches]
159
+
160
+ def get_schema(self, name: str) -> Optional[Schema]:
161
+ """Get a schema by name"""
162
+ for schema in self.schemas:
163
+ if schema.name == name:
164
+ return schema
165
+ return None
166
+
167
+ def add_schema(self, schema: Schema) -> bool:
168
+ """Add a custom schema"""
169
+ if self.get_schema(schema.name):
170
+ return False
171
+ self.schemas.append(schema)
172
+ return True
173
+
174
+ def suggest_followup(self, schema_name: str) -> List[str]:
175
+ """
176
+ Suggest follow-up questions/actions for a schema.
177
+
178
+ Args:
179
+ schema_name: Name of detected schema
180
+
181
+ Returns:
182
+ List of suggestions
183
+ """
184
+ suggestions = {
185
+ "problem_solving": [
186
+ "What is the root cause?",
187
+ "What solutions have been tried?",
188
+ "What resources are available?"
189
+ ],
190
+ "planning": [
191
+ "What is the timeline?",
192
+ "Who is responsible?",
193
+ "What could go wrong?"
194
+ ],
195
+ "learning": [
196
+ "How can this be applied elsewhere?",
197
+ "What would you do differently?",
198
+ "Who else should know this?"
199
+ ],
200
+ "inquiry": [
201
+ "What do you already know?",
202
+ "Where can you find more information?",
203
+ "Who might have the answer?"
204
+ ],
205
+ "reflection": [
206
+ "What changed since then?",
207
+ "What did you learn?",
208
+ "How does this affect the future?"
209
+ ],
210
+ "decision": [
211
+ "What alternatives were considered?",
212
+ "What are the trade-offs?",
213
+ "When will you reevaluate?"
214
+ ]
215
+ }
216
+
217
+ return suggestions.get(schema_name, [])
@@ -0,0 +1,179 @@
1
+ """
2
+ MemoryCoreClaw - Working Memory Module
3
+
4
+ Implements limited-capacity temporary storage (7±2 model).
5
+ """
6
+
7
+ from datetime import datetime, timedelta
8
+ from typing import Optional, Any, Dict, List
9
+ from dataclasses import dataclass
10
+ import json
11
+
12
+
13
+ @dataclass
14
+ class WorkingItem:
15
+ """An item in working memory"""
16
+ key: str
17
+ value: Any
18
+ priority: float
19
+ created_at: datetime
20
+ expires_at: Optional[datetime] = None
21
+ access_count: int = 0
22
+
23
+
24
+ class WorkingMemory:
25
+ """
26
+ Working Memory Engine
27
+
28
+ Based on Baddeley's working memory model with capacity limit.
29
+
30
+ Features:
31
+ - Limited capacity (default 9 items, 7±2)
32
+ - Priority-based eviction
33
+ - TTL support
34
+ - Access tracking
35
+
36
+ Usage:
37
+ wm = WorkingMemory()
38
+ wm.hold("task", "processing", priority=0.9)
39
+ task = wm.retrieve("task")
40
+ """
41
+
42
+ DEFAULT_CAPACITY = 9 # 7±2 model
43
+
44
+ def __init__(self, capacity: int = DEFAULT_CAPACITY):
45
+ """
46
+ Initialize working memory.
47
+
48
+ Args:
49
+ capacity: Maximum items (default 9)
50
+ """
51
+ self.capacity = capacity
52
+ self.items: Dict[str, WorkingItem] = {}
53
+ self.stats = {
54
+ 'adds': 0,
55
+ 'retrieves': 0,
56
+ 'evictions': 0,
57
+ 'expirations': 0
58
+ }
59
+
60
+ def hold(
61
+ self,
62
+ key: str,
63
+ value: Any,
64
+ priority: float = 0.5,
65
+ ttl_seconds: int = None
66
+ ) -> bool:
67
+ """
68
+ Store an item in working memory.
69
+
70
+ Args:
71
+ key: Item key
72
+ value: Item value
73
+ priority: Priority (0-1, higher = less likely to evict)
74
+ ttl_seconds: Time to live in seconds
75
+
76
+ Returns:
77
+ True if stored successfully
78
+ """
79
+ # Evict if at capacity
80
+ if len(self.items) >= self.capacity and key not in self.items:
81
+ self._evict_lowest_priority()
82
+
83
+ expires_at = None
84
+ if ttl_seconds:
85
+ expires_at = datetime.now() + timedelta(seconds=ttl_seconds)
86
+
87
+ self.items[key] = WorkingItem(
88
+ key=key,
89
+ value=value,
90
+ priority=priority,
91
+ created_at=datetime.now(),
92
+ expires_at=expires_at
93
+ )
94
+
95
+ self.stats['adds'] += 1
96
+ return True
97
+
98
+ def retrieve(self, key: str) -> Optional[Any]:
99
+ """
100
+ Retrieve an item from working memory.
101
+
102
+ Args:
103
+ key: Item key
104
+
105
+ Returns:
106
+ Item value or None if not found/expired
107
+ """
108
+ if key not in self.items:
109
+ return None
110
+
111
+ item = self.items[key]
112
+
113
+ # Check expiration
114
+ if item.expires_at and datetime.now() > item.expires_at:
115
+ del self.items[key]
116
+ self.stats['expirations'] += 1
117
+ return None
118
+
119
+ item.access_count += 1
120
+ self.stats['retrieves'] += 1
121
+ return item.value
122
+
123
+ def forget(self, key: str) -> bool:
124
+ """Remove an item"""
125
+ if key in self.items:
126
+ del self.items[key]
127
+ return True
128
+ return False
129
+
130
+ def clear(self) -> int:
131
+ """Clear all items"""
132
+ count = len(self.items)
133
+ self.items.clear()
134
+ return count
135
+
136
+ def _evict_lowest_priority(self):
137
+ """Evict the lowest priority item"""
138
+ if not self.items:
139
+ return
140
+
141
+ # Find lowest priority item
142
+ lowest_key = min(self.items.keys(), key=lambda k: self.items[k].priority)
143
+ del self.items[lowest_key]
144
+ self.stats['evictions'] += 1
145
+
146
+ def get_all(self) -> List[Dict]:
147
+ """Get all items"""
148
+ self._clean_expired()
149
+ return [
150
+ {
151
+ 'key': item.key,
152
+ 'value': item.value,
153
+ 'priority': item.priority,
154
+ 'created_at': item.created_at.isoformat(),
155
+ 'expires_at': item.expires_at.isoformat() if item.expires_at else None
156
+ }
157
+ for item in self.items.values()
158
+ ]
159
+
160
+ def _clean_expired(self):
161
+ """Remove expired items"""
162
+ now = datetime.now()
163
+ expired = [
164
+ key for key, item in self.items.items()
165
+ if item.expires_at and now > item.expires_at
166
+ ]
167
+ for key in expired:
168
+ del self.items[key]
169
+ self.stats['expirations'] += 1
170
+
171
+ def get_stats(self) -> Dict:
172
+ """Get statistics"""
173
+ self._clean_expired()
174
+ return {
175
+ 'capacity': self.capacity,
176
+ 'used': len(self.items),
177
+ 'utilization': len(self.items) / self.capacity,
178
+ 'stats': self.stats
179
+ }